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/binder2nd 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With