开发者

Rework for loop over STL container to use functional techniques

I have a std::vector of pointers Person objects, which have a member function std::string getName() const. Using STL algorithms I want to count all the Person objects in the vector where getName() returns "Chad".

The behaviour simply iterating over the loop would be:

int num_chads = 0;
for(std::vector<Person *>::const_iterator it = vec.begin(); it != vec.end(); ++it)
{
    if((*it)->getName() == "Chad")
        ++num_chads;
}

I want to rework this so it uses all STL algorithms and functors etc (make it more functional-oriented). This is what I think I need to do:

const int num_chads = std::count_if(vec.begin(), vec.end(),
                                    std::bind1st(std::bind2nd(std::equal_to, mem_fun(Person::getName)), "Chad"));

As you can probably tell this doesn't work. Firstly, as I understand it, you can't use bind1st/bind2nd on binder1st/bin开发者_高级运维der2nd objects as they are specifically designed to work with std::binary_functions. Secondly, and much more importantly, I don't think I am using the correct technique. I do want to bind one of the arguments to "Chad", but with the iterator argument I actually just want to transform the iterator value to a string before calling the bound version of equals_to.

I think it is possible to do this using Boost, but is it possible using just the core C++03 (i.e. no C++0x lambas!)?

EDIT: Can anyone come up with an example which does not use a user-defined predicate (i.e. just using the tools provided in the std toolkit)?

EDIT: While Matthieu's answer is a textbook answer for how to use functors in STL algorithms, Cubbi's answer came from the approach I was looking for (although Mathieu did answer before I edited the question to make it more specific, so apologies there!).


I have always found lambdas relatively unreadable. I much prefer to write explicit types:

struct Named
{
  Named(char const* ref): _ref(ref) {}
  bool operator()(Person* p) const { return p && p->getName() == _ref; }
  char const* _ref;
};

size_t const c = std::count_if(vec.begin(), vec.end(), Named("Chad"));

Though the definition of Named is "out-of-line", a properly chosen name conveys the intention and hides the implementation details. Personally, I consider this a good thing, because then I am not distracting by implementation details or trying to figure out what's going on by reverse engineering the code (as evident as it might be).


Since nobody posted the actual boost code yet, C++98 with boost:

ptrdiff_t num_chads = std::count_if(vec.begin(), vec.end(),
                      boost::bind(&Person::getName, _1) == "Chad");

test run https://ideone.com/PaVJe

As for pure C++, I don't think it's possible without the compose1 adaptor, present in STL but not in C++ stdlib...

and here it is (using GCC's implementation of STL)

ptrdiff_t num_chads = std::count_if(vec.begin(), vec.end(),
                     __gnu_cxx::compose1(
                         std::bind2nd(std::equal_to<std::string>(), "Chad"),
                         std::mem_fun(&Person::getName)));

test run: https://ideone.com/EqBS5

EDIT: corrected to account for Person*


Use boost::bind, it's superior by quite some way to the existing Standard binding mechanisms. boost::bind is completely C++03 compatible.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜