(C ++) What's the difference between these overloaded operator functions?

What is the difference between these two ways of overloading the! = Operator below. What's better?

Class Test  
{  
...//
private:  
int iTest

public:  
BOOL operator==(const &Test test) const;
BOOL operator!=(const &Test test) const;  
}

BOOL operator==(const &Test test) const  
{  
    return (iTest == test.iTest);  
}    

//overload function 1  
BOOL Test::operator!=(const &Test test) const  
{  
    return !operator==(test);  
}

//overload function 2  
BOOL Test::operator!=(const &Test test) const  
{  
    return (iTest != test.iTest);  
}  

      

I recently saw the function 1 syntax for calling the operator sibling function and wonder if it provides his way of getting any benefits.

+2


a source to share


6 answers


Your first overload will ensure that a call !=

on your type will always provide the opposite of the call ==

, even if your implementation ==

changes.

The second overloaded function does not work as it is possible to provide any implementation for ==

and modify the existing implementation in the future.

If you want to make sure it !=

will always be the opposite ==

, go to the first one (at the cost of an extra function call, which can become very inline).



Here's a good example. Suppose you have a class Point2D

with fields x

and y

. If you want to implement ==

, you have to compare in field x

and field y

. If you implement !=

by calling operator==

, you have shorter code and one less function if you ever moved to polar representation.

Equality tests and comparisons are always susceptible to maintenance errors as the class fields change. Minimizing the number of directly stateful methods can reduce the risk of errors.

+7


a source


They will almost certainly compile to the same machine code.

I prefer choice 2, simply because I am embarrassed to say "operator ==". But you could use



return ! (*this == test)

      

And IMHO, which are also clear and understandable.

+2


a source


I can think of many reasons (or perhaps aspects of the same reason), write like this. They all boil down to this: DRY .

  • This ensures that the two objects are always either ==

    or!=

  • If you decide to change what is in the class or what is used for equality testing, you only need to change it in one place.

I think conceptually you actually have two different things that you define here:

  • Defining "equality" for the Test class

  • A useful interface that people using this class can use to determine the equality of their instances

With method 2, you are doing both ad-hoc. With method 1, you define equality in operator==

and expose the rest of the interface through operator!=

.

Some languages ​​/ libraries take it even more, for example in Ruby you can define only <=>

to compare ordered objects and mix-in Comparable and get equality, inequality and between?

.

+2


a source


Version # 1, while syntactically ugly IMHO, allows you to change the equality logic in one place (in the == operator overload). This ensures that the two overloads are always in sync.

+1


a source


In general, the following statement:
  return !(*this == object);


allows you to define !=

in terms of one function. In the world of inheritance, child objects should operator==

only be defined to use the base class operator!=

:

struct Base
{
  virtual bool isEqual(const Base& other) const = 0;
  bool  operator==(const Base& other) const
  {
    return isEqual(other);
  }
  bool  operator!=(const Base& other) const
  {
    return !(*this == other);  // Uses Base::operator==
  }
};

      

In the above base class, defining operator!=

with !=

will require descendants to implement more methods.

It also !(*this == other)

allows you to define a global common function for !=

:

template <typename T>
bool operator!=(const T& a, const T& b)
{
  return !(a == b);
}

      

Despite the fact that this pattern does not give much importance to ==

and !=

differences when using relational operators are different: <, <=, >, >=

.

+1


a source


In general, it is always best to implement related functions in terms of each other. In the case of operators, there are always groups. For example, it !=

can be implemented in terms of ==

; post-increment can be implemented in terms of pre-increment; >

, <=

and >=

can be implemented in terms <

(see std::rel_ops

in <utility>

), etc. If something about the class needs to be changed, you only need to change the underlying statements and everything else will automatically update to reflect the new behavior.

The general implementation of all "secondary" operators is always the same, and therefore there is even a library that automatically provides operators that are defined in terms of several provided. See Boost.Operators

Your example:

#include <boost/operators.hpp>

class Test : boost::equality_comparable<Test, Test>
{
private:
    int iTest;

public:
    bool operator==(const Test& test) const;
    //look, no operator !=
};

int main()
{
    Test a, b;
    a != b; //still works
}

      

0


a source







All Articles