Malcolm
Malcolm

Reputation: 752

Overriding template function in specialized daughter class

I have a templated class MatchBase with a function for the operator == as such

template<typename Element>
class MatchBase{
    virtual bool operator ==(const MatchBase<Element>& m) const{
    if(_v1 == m.getFirst() && _v2 == m.getSecond()){
        return true;
    }
    return false;
}

I know have a daughter class Match that is template specialized. The class Place used for the specialization does not have an operator== to do the comparison. Thus I'm trying to override the operator== to work with the class Place.

On the things I have tried :

class Match : public betterGraph::MatchBase<graphmatch::Place>{
public :
    Match(const graphmatch::Place& v, const graphmatch::Place& vv) : 
        betterGraph::MatchBase<graphmatch::Place>(v, vv) 
        {};

    virtual bool operator ==(const Match& m) const{
        if(_v1.mass_center == m.getFirst().mass_center && _v2.mass_center == m.getSecond().mass_center){
            return true;
        }
        return false;
    }

};

I also tried

virtual bool operator ==(const betterGraph::MatchBase<graphmatch::Place>& m) const{
    if(_v1.mass_center == m.getFirst().mass_center && _v2.mass_center == m.getSecond().mass_center){
        return true;
    }
    return false;
}

But I always hit an error of the type :

error: no match for ‘operator==’ (operand types are ‘const AASS::graphmatch::Place’ and ‘const AASS::graphmatch::Place’)
if(_v1 == m.getFirst() && _v2 == m.getSecond()){

Because it tries to compile the method from the Base class.

Is there any way for me to override this function of the base class in the daughter class ? I've read the question here but here it's the method that is specialized while my class is specialized so I don't see how to do a forward declaration :/.

Upvotes: 0

Views: 111

Answers (1)

Simon Kraemer
Simon Kraemer

Reputation: 5680

The function may be virtual but it's still initialized when you inherit your base class. This is essential as you might write something like this:

MatchBase<Place> test = Match(p1,p2);

MatchBase<Place> is the base class of Match yet they are not the same. Calling MatchBase<Place>::operator==() will still call the function defined in your template base class.

You have now multiple option: - make the function in the base class a pure virtual - implement Place::operator==() - pass a comperator as argument to your base class as argument

The first two should be clear (if not please ask). For the third this might be a one possible way to do it:

template<typename Element, typename Less = std::less<Element>>
class MatchBase {

protected:
    Element _v1;
    Element _v2;

public:
    MatchBase(const Element& v, const Element& vv) : _v1(v), _v2(vv)
    {}

    virtual bool operator ==(const MatchBase<Element, Less>& m) const {
        Less less;
        bool v1Equal = !less(_v1, m.getFirst()) && !less(m.getFirst(), _v1);
        bool v2Equal = !less(_v2, m.getSecond()) && !less(m.getSecond(), _v2);
        return v1Equal && v2Equal;
    }

    const Element& getFirst() const { return _v1; }
    const Element& getSecond() const { return _v2; }

};

struct Place
{
    int mass_center;
};

struct PlaceLess
{
    bool operator()(const Place& p1, const Place& p2) 
    {
        return p1.mass_center < p2.mass_center; 
    };
};

class Match : public MatchBase <Place, PlaceLess>
{
public:
    Match(const Place& v, const Place& vv) :
        MatchBase<Place, PlaceLess>(v, vv)
    {};
};

Another way might be to specialize std::less<T> in this context. So you won't need to pass it as template parameter.

template<typename Element>
class MatchBase {

protected:
    Element _v1;
    Element _v2;

public:
    MatchBase(const Element& v, const Element& vv) : _v1(v), _v2(vv)
    {}

    virtual bool operator ==(const MatchBase<Element>& m) const {
        std::less<Element> less;
        bool v1Equal = !less(_v1, m.getFirst()) && !less(m.getFirst(), _v1);
        bool v2Equal = !less(_v2, m.getSecond()) && !less(m.getSecond(), _v2);
        return v1Equal && v2Equal;
    }

    const Element& getFirst() const { return _v1; }
    const Element& getSecond() const { return _v2; }

};

struct Place
{
    int mass_center;
};

template<>
struct std::less<Place>
{
    bool operator()(const Place& p1, const Place& p2) 
    {
        return p1.mass_center < p2.mass_center; 
    };
};

class Match : public MatchBase <Place>
{
public:
    Match(const Place& v, const Place& vv) :
        MatchBase<Place>(v, vv)
    {};
};

Of course you could merge these ways so you might override the Less template parameter if needed.

If you don't plan on using predefined types (thinking of int, std::string, etc...) you could also make sure that the class passed as Element must inherit a class/struct that enforces that operator== is implemented:

template <typename T>
struct IComparable
{
    virtual bool operator==(const T& other) const = 0;
};


template<typename Element>
class MatchBase {

    static_assert(std::is_base_of<IComparable<Element>, Element>::value, "Element must implement comparable");

protected:
    Element _v1;
    Element _v2;

public:
    MatchBase(const Element& v, const Element& vv) : _v1(v), _v2(vv)
    {}

    virtual bool operator ==(const MatchBase<Element>& m) const {
        return _v1 == m._v1 && _v2 == m._v2;
    }
};

struct Place : public IComparable<Place>
{
    int mass_center;

    bool operator==(const Place& other) const
    {
        return mass_center == other.mass_center;
    };
};

class Match : public MatchBase <Place>
{
public:
    Match(const Place& v, const Place& vv) :
        MatchBase<Place>(v, vv)
    {};
};

Upvotes: 0

Related Questions