views:

166

answers:

3

I am confused about the interface of std::find. Why doesn't it take a Compare object that tells it how to compare two objects?

If I could pass a Compare object I could make the following code work, where I would like to compare by value, instead of just comparing the pointer values directly:

typedef std::vector<std::string*> Vec;
Vec vec;
std::string* s1 = new std::string("foo");
std::string* s2 = new std::string("foo");
vec.push_back(s1);
Vec::const_iterator found = std::find(vec.begin(), vec.end(), s2);
// not found, obviously, because I can't tell it to compare by value
delete s1;
delete s2;

Is the following the recommended way to do it?

template<class T>
struct MyEqualsByVal {
  const T& x_;
  MyEqualsByVal(const T& x) : x_(x) {}
  bool operator()(const T& y) const {
    return *x_ == *y;
  }
};
// ...
vec.push_back(s1);
Vec::const_iterator found = 
    std::find_if(vec.begin(), vec.end(),
                 MyEqualsByVal<std::string*>(s2)); // OK, will find "foo"
+2  A: 

Since your T is a pointer, you may as well store a copy of the pointer in the function object.

Other than that, that is how it is done and there's not a whole lot more to it.

As an aside, it's not a good idea to store bare pointers in a container, unless you are extremely careful with ensuring exception safety, which is almost always more hassle than it's worth.

James McNellis
And... I totally missed the first half of the question... oops. Though, Steve Jessop's answer is better than I could have explained it anyway.
James McNellis
A: 

That's exactly what find_if is for - it takes a predicate that is called to compare elements.

AshleysBrain
+5  A: 

find can't be overloaded to take a unary predicate instead of a value, because it's an unconstrained template parameter. So if you called find(first, last, my_predicate), there would be a potential ambiguity whether you want the predicate to be evaluated on each member of the range, or whether you want to find a member of the range that's equal to the predicate itself (it could be a range of predicates, for all the designers of the standard libraries know or care, or the value_type of the iterator could be convertible both to the predicate type, and to its argument_type). Hence the need for find_if to go under a separate name.

find could have been overloaded to take an optional binary predicate, in addition to the value searched for. But capturing values in functors, as you've done, is such a standard technique that I don't think it would be a massive gain: it's certainly never necessary since you can always achieve the same result with find_if.

If you got the find you wanted, you'd still have to write a functor (or use boost), since <functional> doesn't contain anything to dereference a pointer. Your functor would be a little simpler as a binary predicate, though, or you could use a function pointer, so it'd be a modest gain. So I don't know why this isn't provided. Given the copy_if fiasco I'm not sure there's much value in assuming there are always good reasons for algorithms that aren't available :-)

Steve Jessop
Thanks! Out of curiosity, what is wrong with `copy_if`?
@dehmann: The only thing wrong with it is that it's not in the standard. It got left out essentially due to an editing accident.
Steve Jessop