Reputation: 9925
I’ve run into a speedbump while using the STL with what seems like a normal scenario, simplified here:
class Person {
string Name;
int Age;
};
vector<Person> people;
AddPeople(people);
string s("Bob");
find(people.begin(), people.end(), s);
Unfortunately find
wants to compare the entire class.
Is there a better or more appropriate way to do this the “STL way”? The suggested questions weren’t helpful, but I managed to find a couple of related questions but no direct solution.
There’s some potential work-arounds:
Forgo find
altogether (cluttered, but could be refactored):
bool bBob = false; for (UINT i = 0; i < people.size(); i++) { if (people[i].Name == s) bBob = true; break; }
Provide conversion operator (implicit conversion doesn’t work; explicit can’t be used in find
):
class Person { string Name; int Age; operator string() {return Name;} };
Person b ("Bob", 99); string s ("Bob"); b == s; //doesn’t work string(b) == s; //works, but no good for find()
Define a standalone equality operator (simple, effective, but globally exposed):
BOOL operator==(Person l, string r) { return l.Name == r; }
Define a member equality operator (makes comparison order dependent; object must be first):
class Person { string Name; int Age; bool operator==(string s) {return Name == s;} };
Person b ("Bob", 99); string s ("Bob"); b==s; //works s==b; //doesn’t work, but not a problem for find()
It looks like #4 is the best candidate, but none seem ideal or feel “STL”, and some have problems.
Upvotes: 6
Views: 22314
Reputation: 318
I guess you want these for subclasses or classes that share a property called name, if not you could create a simple function yourself. If you do want it to work for any class that has a property name you could create a template like this:
template < class ClassWithNameProperty >
ClassWithNameProperty * searchName (std::string name,
std::vector<ClassWithNameProperty *> array)
{
for (ClassWithNameProperty * obj: array)
{
if (obj.compare(obj->name))
{
return obj;
}
}
return NULL;
}
Or however you want your search function to work, hope this helps you
Upvotes: 1
Reputation: 126432
Is there a better or more appropriate way to do this the “STL way”?
You can use std::find_if
(powered by C++11 lambdas):
std::string name = "Bob";
// ...
std::find_if(std::begin(people), std::end(people),
[&] (Person const& p) { return p.Name == name; }
Notice, that calling it "STL way" is inappropriate. This is the C++ Standard Library, not the STL ("Standard Template Library"). The STL served as a strong inspiration for the Containers and Algorithms Library of the C++ Standard Library, but the two things are not the same. See this Q&A on StackOverflow for further information.
EDIT:
Since you are using a compiler that does not support lambdas, you can define your own functor predicate:
struct person_has_name
{
person_has_name(std::string const& n) : name(n) { }
bool operator () (Person const& p) { return p.Name == name; }
private:
std::string name;
};
And use it with std::find_if
this way:
std::string name = "Bob";
// ...
std::find_if(people.begin(), people.end(), person_has_name(name));
Upvotes: 10
Reputation: 409166
There are a couple of ways to do it, all involving some kind of callable objects and std::find_if
.
The first is to use the new C++11 lambda:
std::find_if(people.begin(), people.end(), [](const Person& person)
{ return person.Name == "Bob"; });
If you have an older compiler that doesn't support lambdas, you could use a functor object:
class FindPersonByName
{
std::string name;
public:
FindPersonByName(const std::string& name) : name(name) {}
bool operator()(const Person& person) const
{ return person.Name == name; }
};
std::find_if(people.begin(), people.end(), FindPersonByName("Bob"));
Of course both of these requires your class to have the Name
member public. But you can change it to use a public GetName
function instead and add that to the class.
Upvotes: 1