1

Something that I have to do quite often is finding a member in a collection of elements which has an element with a given value. For example given:

class Person
{
   string getName() const {return mName;}
   private:
   string mName;
};

std::vector<Person> people;

I want to find the Person whose name is "Alice". One way to do this would be (using boost range adaptors):

string toFind = "Alice";
auto iterator = find(people | transformed([](Person const & p){p.getName()}) , toFind );

this is a lot of boilerplate for such a simple operation. Shouldn't it be possible to do something like:

string toFind = "Alice";
auto iterator = find(people | transformed(&Person::getName) , toFind ); 

(doesnt compile because &Person::getName is not a unary function)

Is there an easy way to get a unary function for a member?

xskxzr
  • 12,442
  • 12
  • 37
  • 77
Llopeth
  • 406
  • 5
  • 11

2 Answers2

3

You can use the std::find_if function with appropriate lambda which checks if the getName() value is equal to tofind string:

std::string tofind = "Alice";
auto it = std::find_if(people.begin(), people.end(), [&tofind](const Person& o) {return o.getName() == tofind; });
if (it != std::end(people)) {
    std::cout << "Contains: " << tofind << '\n';
}
else {
    std::cout << "Does not contain: " << tofind << '\n';
}

This would require the getName function to be in a public class scope and you should provide the appropriate constructor. Full code would be:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

class Person {
public:
    Person(const std::string& name) : mName{ name } {}
    std::string getName() const { return mName; }
private:
    std::string mName;
};

int main() {
    std::vector<Person> people{ {"John"}, {"Alice"}, {"Jane"} };
    std::string tofind = "Alice";
    auto it = std::find_if(people.begin(), people.end(), [&tofind](const Person& o) {return o.getName() == tofind; });
    if (it != std::end(people)) {
        std::cout << "Contains: " << tofind << '\n';
    }
    else {
        std::cout << "Does not contain: " << tofind << '\n';
    }
}
Ron
  • 14,674
  • 4
  • 34
  • 47
  • Hi Ron!, yes that is fine, but I guess it carries too much boilerplate, I would like something more compact, like: find_if ( people , unary_compare(&Person::getName,alice) ) – Llopeth Mar 09 '18 at 11:48
1

What you are looking for is std::mem_fn. It wraps a pointer to member function such that one can call it like free function, where the object on which the member function is called is passed as the first argument. In your example, you can use it like:

string toFind = "Alice";    
auto iterator = find(people | transformed(std::mem_fn(&Person::getName)) , toFind ); 
                                       // ^^^^^^^^^^^
xskxzr
  • 12,442
  • 12
  • 37
  • 77