struct Equal
{
friend bool operator==(const Equal&, const Equal&);
};
struct Unequal
{
friend bool operator!=(const Unequal&, const Unequal&);
};
int main()
{
Equal{} == Equal{}; // OK
Equal{} != Equal{}; // OK
Unequal{} == Unequal{}; // Error
Unequal{} != Unequal{}; // OK
}
The Equal{} != Equal{}
expression in the code above is successfully transformed into something like !(Equal{} == Equal{})
, so why can't the same thing be done with Unequal{} == Unequal{}
?
As mentioned in a comment the way !=
is synthesized from ==
is described here. Specifically
- For
x != y
, all member, non-member, and built-inoperator==
s found are added to the set, unless there is a matchingoperator!=
.- For equality operator expressions
x == y
andx != y
, a synthesized candidate with the order of the two parameters reversed is added for each member, non-member, and built-inoperator==
s found, unless there is a matchingoperator!=
.
That is, only !=
is synthesized from ==
but not the other way around. This is how the language is defined.
Consider that this is a rather recent addition to the language. Prior to C++20
, Equal{} != Equal{};
would have failed (and perhaps we didn't even reach the point to wonder why Unequal{} == Unequal{};
fails too, why would it not).
For, why ==
is not synthesized from !=
I can only speculate.
In my opinion the current way it is specified is sound: Either you choose to have straightforward comparison operators where !(a == b) == (a!=b)
. In that case you implement operator==
and let operator!=
be synthesized. If for whatever reason a != b
is not just !(a == b)
you can opt-out and implement your custom operator!=
in addition to your operator==
. There is one way to use the default and one way to opt-out. Having two ways to do the same would add complexity for no gain.