Where should the if = operator be in the 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, the! = Operator is defined in the base class. Since I am very lazy, I do not want to duplicate such simple code in all 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 A::operator== , not B::operator== (even if the operator is virtual, since the version parameter of the derived class is different, they not related in vtable).

So what's the best way to address? = operator in the general case for a class hierarchy?

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

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

But it’s a pain when you have a lot of activities .... 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 can be risky if you declare it differently (or if you declare == itself calling != ), It will end up with an infinite loop .. .). Therefore, I believe that this solution is very dangerous ... except that we can restrict the template that will be used for all classes derived from the class of our top-level hierarchy ( A in my example) .... but I don’t think It is doable at all.

Note. I do not use C ++ 11, but ... sorry.

+7
c ++ comparison-operators operator-overloading
source share
2 answers

How about something like that?

 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) equals function and call this function from operator== and operator!= Defined in the base class.

Operators do not need to be overridden in derived classes. To change the comparison in a derived class, simply override equals .

Note that dynamic_cast in the above code is used to ensure that the execution type is a valid type to perform the comparison. I.e. for B::equals he used to have right be B - this is necessary, because otherwise right will not have a member B

+4
source share

Your function in B ...

 virtual bool operator==( const B& right ) const 

... does not cancel the function in A ...

 virtual bool operator==( const A& right ) const 

... because the types of arguments are different. (Differences are allowed only for covariant return types.)

If you fix this, you can choose how to compare objects B with other objects A and A , for example. perhaps:

 bool operator==( const A& right ) const override { if (A::operator==( right )) if (typeid(*this) == typeid(right)) return b == static_cast<const B&>(right).b; return false; } 

Note that using typeid comparisons above means that only B objects will be compared equal: any B will compare unequal with any B aligned object. This may or may not be what you want.

With the implementation for B::operator== existing implementation != Will correctly terminate operator== . As Jarod42 points out, your A::operator== not reliable when the value on the left is set to A , only the fragment A object on the right side will be compared ... you may prefer

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

This has the same problems as B::operator== above: for example. a A would compare unequal with a derived object that would not introduce additional data members.

+5
source share

All Articles