3

I want to use std::find on a list of a shared_ptr of an abstract class, but I'm getting an error. Is there even a way to compare two shared_ptr by dereferencing them in std::find?

Is it possible to maybe make a friend operator== that overloads shared_ptr<A>?

Minimal example:

#include "point.h"
#include <list>
#include <algorithm>
#include <memory>

using namespace std;

class A {

protected:
    Point loc;
public:

    virtual void foo() = 0;

    virtual bool operator==(const Point& rhs) const = 0;
};

class B: public A {
    virtual void foo() override{}

    virtual bool operator==(const Point& rhs) const override {
        return rhs == loc;
    }
};

class C {

    list<shared_ptr<A>> l;
    void bar(Point & p) {

        const auto & f = find(l.begin(), l.end(), p); //<-- error is from here
    }
};

Error C2679 binary '==': no operator found which takes a right-hand operand of type 'const Point' (or there is no acceptable conversion)

Note: Point already has operator==.

shinzou
  • 5,850
  • 10
  • 60
  • 124

2 Answers2

5

Problem:

find() is designed to find one precise value in an iterator range.

You have defined an operator== to compare an A with a Point. But your list doesn't contain A objects but shared pointers to A objects. Unfortunately comparing a shared pointer to a Point is not something that is defined. This mismatch causes the error that you've reported.

Solution:

An easy solution would be to use find_if() instead of find(): it doesn't look for a precise value, but for a predicate to become true:

   const auto & f = find_if(l.begin(), l.end(),[p](shared_ptr<A> &a){ return *a==p; });
Community
  • 1
  • 1
Christophe
  • 68,716
  • 7
  • 72
  • 138
  • 1
    It compiles. I knew lambda functions are cool, too bad our instructor dropped them, I'm gonna read up on them now. – shinzou Jan 07 '16 at 19:32
  • Yes, lambdas are definitively cool. Here a [quick intro](http://www.drdobbs.com/cpp/lambdas-in-c11/240168241) to start with. – Christophe Jan 07 '16 at 19:39
1

std::find can be implemented as

template<class InputIt, class T>
InputIt find(InputIt first, InputIt last, const T& value)
{
    for (; first != last; ++first) {
        if (*first == value) {
            return first;
        }
    }
    return last;
}

As you can see it is comparing *first == value which is translates to a shared_ptr<A> == Point when using find(l.begin(), l.end(), p). Since it is going to use shared_ptr<A>::operator== you are going to use std::find_if and write a custom compare function/functor that can compare those two types and pass it to find

You can learn more about functors at: C++ Functors - and their uses

Community
  • 1
  • 1
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • Or to pass special function to `find` – Slava Jan 07 '16 at 19:01
  • I have no clue about functors, does the special function for `find` have to be a functor? – shinzou Jan 07 '16 at 19:16
  • @kuhaku It doesn't have to be. You can make a normal function and pass it to `find` as well. Functors are suggested as they can be inlined which can make the code faster. – NathanOliver Jan 07 '16 at 19:17
  • As far as I know, compare function cannot be argument of `find`. http://www.cplusplus.com/reference/algorithm/find/ – Rames Jan 07 '16 at 19:22