Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the right way to overload operator== for a class hierarchy?

Suppose I have the following class hierarchy:

class A {     int foo;     virtual ~A() = 0; };  A::~A() {}  class B : public A {     int bar; };  class C : public A {     int baz; }; 

What's the right way to overload operator== for these classes? If I make them all free functions, then B and C can't leverage A's version without casting. It would also prevent someone from doing a deep comparison having only references to A. If I make them virtual member functions, then a derived version might look like this:

bool B::operator==(const A& rhs) const {     const B* ptr = dynamic_cast<const B*>(&rhs);             if (ptr != 0) {         return (bar == ptr->bar) && (A::operator==(*this, rhs));     }     else {         return false;     } } 

Again, I still have to cast (and it feels wrong). Is there a preferred way to do this?

Update:

There are only two answers so far, but it looks like the right way is analogous to the assignment operator:

  • Make non-leaf classes abstract
  • Protected non-virtual in the non-leaf classes
  • Public non-virtual in the leaf classes

Any user attempt to compare two objects of different types will not compile because the base function is protected, and the leaf classes can leverage the parent's version to compare that part of the data.

like image 293
Michael Kristofik Avatar asked Nov 06 '09 22:11

Michael Kristofik


People also ask

What is the correct way to overload operator?

When a binary operator is overloaded the corresponding assignment operator, if any, must be explicitly overloaded. We can use the default equality operator in an overloaded implementation of the equality operator. A public or nested public reference type does not overload the equality operator.

Can we overload == operator?

You cannot overload an operator working on fundamental types. That is, you can't overload the '+' operator for two int s (fundamental type) to perform subtraction. You cannot change the syntax rules (such as associativity, precedence and number of arguments) of the overloaded operator.

Which function can be used to perform overload of == operator?

Operators Overloading in C++ Overloaded operators are functions with special names: the keyword "operator" followed by the symbol for the operator being defined. Like any other function, an overloaded operator has a return type and a parameter list.

Can we overload == operator in C++?

You can redefine or overload the function of most built-in operators in C++. These operators can be overloaded globally or on a class-by-class basis. Overloaded operators are implemented as functions and can be member functions or global functions. An overloaded operator is called an operator function.


2 Answers

For this sort of hierarchy I would definitely follow the Scott Meyer's Effective C++ advice and avoid having any concrete base classes. You appear to be doing this in any case.

I would implement operator== as a free functions, probably friends, only for the concrete leaf-node class types.

If the base class has to have data members, then I would provide a (probably protected) non-virtual helper function in the base class (isEqual, say) which the derived classes' operator== could use.

E.g.

bool operator==(const B& lhs, const B& rhs) {     return lhs.isEqual( rhs ) && lhs.bar == rhs.bar; } 

By avoiding having an operator== that works on abstract base classes and keeping compare functions protected, you don't ever get accidentally fallbacks in client code where only the base part of two differently typed objects are compared.

I'm not sure whether I'd implement a virtual compare function with a dynamic_cast, I would be reluctant to do this but if there was a proven need for it I would probably go with a pure virtual function in the base class (not operator==) which was then overriden in the concrete derived classes as something like this, using the operator== for the derived class.

bool B::pubIsEqual( const A& rhs ) const {     const B* b = dynamic_cast< const B* >( &rhs );     return b != NULL && *this == *b; } 
like image 176
CB Bailey Avatar answered Oct 06 '22 00:10

CB Bailey


I was having the same problem the other day and I came up with the following solution:

struct A {     int foo;     A(int prop) : foo(prop) {}     virtual ~A() {}     virtual bool operator==(const A& other) const     {         if (typeid(*this) != typeid(other))             return false;          return foo == other.foo;     } };  struct B : A {     int bar;     B(int prop) : A(1), bar(prop) {}     bool operator==(const A& other) const     {         if (!A::operator==(other))             return false;          return bar == static_cast<const B&>(other).bar;     } };  struct C : A {     int baz;     C(int prop) : A(1), baz(prop) {}     bool operator==(const A& other) const     {         if (!A::operator==(other))             return false;          return baz == static_cast<const C&>(other).baz;     } }; 

The thing I don't like about this is the typeid check. What do you think about it?

like image 41
mtvec Avatar answered Oct 05 '22 22:10

mtvec