Reputation: 21
What is the difference between these two ways of overloading the != operator below. Which is consider 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've just recently seen function 1's syntax for calling a sibling operator function and wonder if writing it that way provides any benefits.
Upvotes: 2
Views: 537
Reputation: 41351
In general, it is always better to implement related functionality in terms of each other. In case of operators, there are always groups. For example, !=
can be implemented in terms of ==
; post-increment can be implemented in terms of pre-increment; >
, <=
and >=
can be implemented in terms of <
(see std::rel_ops
in <utility>
) etc. If something about the class needs changing, you only need to modify the core operators, and all the rest will automatically be updated to reflect the new behavior.
The general implementation of all the "secondary" operators is always the same, and so there is even a library which automatically provides operators that are defined in terms of a few provided ones. 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
}
Upvotes: 0
Reputation: 57749
In general, the following statement:
return !(*this == object);
allows you to define !=
in terms of one function. In the world of inheritance, child objects would only need to define operator==
in order 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==
}
};
With the above base class, defining operator!=
using !=
would require descendants to implement more methods.
Also, !(*this == other)
allows one to define a global, generic function for !=
:
template <typename T>
bool operator!=(const T& a, const T& b)
{
return !(a == b);
}
Although this pattern doesn't provide much for ==
and !=
, the differences are larger when using the relational operators: <, <=, >, >=
.
Upvotes: 1
Reputation: 10621
I can think of many reasons (or perhaps aspects of the same reason) to write it that way. What they all boil down to is: it's DRY.
It ensures that two objects are always either ==
or !=
If you decide to change what's in the class, or what's used for equality testing, you only have to change it in one place
I think that conceptually, you really have two different things you're defining here:
A definition of "equality" for class Test
A useful interface by which people using this class can determine equality of their instances
With method 2, you're doing both ad-hoc. With method 1, you're defining equality in operator==
, and providing the rest of the interface via operator!=
.
Some languages/libraries take it even further, e.g., in Ruby you can define just <=>
to compare ordered objects and mix-in Comparable and get equality, inequalities, and between?
.
Upvotes: 2
Reputation: 89859
Your first overload ensures that calling !=
on your type will always provide the opposite of calling ==
even if your implementation of ==
would change.
Your second overloaded function does not, since it is possible to provide any implementation for ==
and change the existing implementation in the future.
If you want to ensure that !=
will always be the opposite of ==
, go with the first (at the cost of an extra function call which may very well become inlined anyway).
Here is a good example. Suppose that you have a class Point2D
with fields x
and y
. If you want to implement ==
, you will have to compare on field x
and on field y
. If you implement !=
by calling operator==
, you have shorter code, and will have one function less to change if you ever moved to a polar representation.
Equality tests and comparisons are always susceptible to maintenance errors as the class fields change. Minimizing the number of methods that directly access state can reduce the risk of errors.
Upvotes: 7
Reputation: 76918
Version #1, while syntacticly ugly IMHO, allows you to change the equality logic in a single place (in the == operator overload). It guarantees the two overloads are always in sync.
Upvotes: 1
Reputation: 13708
They will almost certainly compile to the same machine code.
I prefer choice 2, just because I find it awkward to say "operator==". But you could have used
return ! (*this == test)
And IMHO that's also clear and easy to understand.
Upvotes: 2