Search code examples
c++operator-overloadingcomparison-operators

Where should != operator be defined in a class hierarchy?


Here is a very simple class hierarchy:

class A
{
public:
    A( int _a ) : a( _a ) {}

    virtual bool operator==( const A& right ) const
    {
        return a == right.a;
    }

    virtual bool operator!=( const A& right ) const
    {
        return !( *this == right );
    }

    int a;
};

class B : public A
{
public:
    B( int _a, int _b ) : A( _a ), b( _b ) {}

    virtual bool operator==( const B& right ) const
    {
        return A::operator==( right ) && b == right.b;
    }

    int b;
};

As you can see, operator != is defined in the base class. Because I'm very lazy, I don't want to duplicate such a simple code in all the derived classes.

Unfortunatley, with this code:

A a4(4), a5(5), a4bis(4);
assert( a4 == a4bis );
assert( a4 != a5 );

B b1(4,5), b2(4,6);
assert( !(b1 == b2) );
assert( b1 != b2 ); // fails because B::operator== is not called!

b1 != b2 returns false, because it executes A::operator!= which calls then A::operator== rather than B::operator== (even if the operator is virtual, as derived class version parameter is different, they are not linked in the vtable).

So what's the best way to adress != operator in a generic way for a class hierarchy?

One solution is to repeat it in each class, B would then have:

virtual bool operator!=( const B& right ) const
{
    return !( *this == right );
}

But that's a pain when you have many classes....I have 30....

Another solution would be to have a generic template approach:

template <class T>
bool operator!=( const T& left, const T& right )
{
    return !( left == right );
}

But this bypasses any != operator defined by any class....so it may be risky if one declared it differently (or if one declared a == itself calling !=, it would end up with an infinite loop...). So I feel this solution being very unsafe....except if we can limit the template to be used for all classes derived from the top-level class of our hierarchy (A in my example)....but I don't think this is doable at all.

Note: I'm not using C++11 yet...sorry about that.


Solution

  • How about something like this ?

    class A {
      protected :
        virtual bool equals(const A& right) const {
          return (a == right.a);
        }
    
      public :
        A(int _a) : a(_a) { }
    
        bool operator==(const A& right) const {
          return this->equals(right);
        }
        bool operator!=(const A& right) const {
          return !(this->equals(right));
        }
    
        int a;
    };
    
    class B : public A {
      protected :
        virtual bool equals(const A& right) const {
          if (const B* bp = dynamic_cast<const B*>(&right)) {
            return A::equals(right) && (b == bp->b);
          }
          return false;
        }
    
      public :
        B(int _a, int _b) : A(_a), b(_b) { }
    
        int b;
    };
    

    Move the comparison logic to a separate (virtual) function equals, and call that function from the operator== and operator!= defined in the base class.

    The operators don't need to be re-defined in the derived classes. To change the comparison in a derived class, simply override equals.

    Note that the dynamic_cast in the code above is used to ensure that the runtime type is a valid type for performing the comparison. Ie. for B::equals, it's used to ensure that right is a B - this is necessary because otherwise right would not have a b member.