non-defaulted operator = doesn't generate == and != in C++20
This is by design.
[class.compare.default] (emphasis mine)
3 If the class definition does not explicitly declare an
==
operator function, but declares a defaulted three-way comparison
operator function, an==
operator function is declared implicitly
with the same access as the three-way comparison operator function.
The implicitly-declared==
operator for a class X is an inline
member and is defined as defaulted in the definition of X.
Only a defaulted <=>
allows a synthesized ==
to exist. The rationale is that classes like std::vector
should not use a non-defaulted <=>
for equality tests. Using <=>
for ==
is not the most efficient way to compare vectors. <=>
must give the exact ordering, whereas ==
may bail early by comparing sizes first.
If a class does something special in its three-way comparison, it will likely need to do something special in its ==
. Thus, instead of generating a potentially non-sensible default, the language leaves it up to the programmer.
!= auto generated from == in C++20?
!=
auto generated from==
in C++20?
Is this standard behavior in C++20?
Yes. operator!=
is auto generated from operator==
in C++20.
Furthermore, all four relational operators are generated if you define operator<=>
, and all of the comparison operators are generated if you define operator<=>
as defaulted.
What you want to do in most cases:
struct example
{
std::string a;
int b;
auto operator<=>(const example&) const = default;
};
Why don't C++ compilers define operator== and operator!=?
The compiler wouldn't know whether you wanted a pointer comparison or a deep (internal) comparison.
It's safer to just not implement it and let the programmer do that themselves. Then they can make all the assumptions they like.
Which operators implictly define / generate other operators in C++?
Which operators implicitly define / generate other operators in C++?
There is only one situation in which one operator defines/generates another, and that is when you default operator<=>
you also get a defaulted operator==
. That's the complete list.
Everything else is not based on declaring operators, it is based on rewriting expressions:
- it's not that an
operator!=
is generated fromoperator==
, it's that the expressionx != y
also tries to evaluate as!(x == y)
- it's not that an
operator<
is generated fromoperator<=>
, it's that the expressionx < y
also tries to evaluate as(x <=> y) < 0
In your case, f == g
simply has no operator==
candidate, so it's ill-formed. The original design of <=>
also would try to rewrite this expression as (f <=> g) == 0
(again, not generating operator==
but rather rewriting the expression). But this was shown to have serious performance issues and so it was changed to not do this. You can read more about Comparisons in C++20 here.
In this case, since you're doing member-wise comparison, you can simply:
bool operator==(Foo const&) const = default;
or write it manually if you prefer. Either way, your operator<=>
is missing a const
- it's important that the comparison operators be symmetric.
Why default three-way operator (spaceship =) generates equality operator (==) and user define three-way operator not?
The principle reason why equality and ordering are separated is performance. If you have a type whose ordering operations are user-defined, then more often than not, you can write a user-defined equality test operation that is more efficient at doing equality tests. And therefore, the language should encourage you to write it by not using operator<=>
for direct equality testing.
This only really applies to user-defined ordering/equality operations. Default ordering is member-wise, and default equality operations are also member-wise. And since ordering implies equality, it is reasonable that defaulting ordering also defaults equality.
Yes, they could make people spell it out, but there wasn't really a good reason for that. operator<=>
was meant to make it easy to opt-in to default ordering; making you write two declarations for something that's already implied by one of them doesn't make sense.
Why must I provide 'operator ==' when 'operator =' is enough?
Why must I provide
operator==
whenoperator<=>
is enough?
Well, mainly because it's not enough :-)
Equality and ordering are different buckets when it comes time for C++ to rewrite your statements:
Equality | Ordering | |
---|---|---|
Primary | == | <=> |
Secondary | != | <, >, <=, >= |
Why is operator!= removed in C++20 for many standard library types?
In C++20 the way that the relational operators work was changed, notably with the introduction of the spaceship <=>
operator. In particular, If you only provide operator==
, then a != b
is rewritten to !(a == b)
.
From [over.match.oper]/3.4:
The rewritten candidate set is determined as follows:
- For the relational ([expr.rel]) operators, the rewritten candidates include all non-rewritten candidates for the expression x <=> y.
- For the relational ([expr.rel]) and three-way comparison ([expr.spaceship]) operators, the rewritten candidates also include a synthesized candidate, with the order of the two parameters reversed, for each non-rewritten candidate for the expression y <=> x.
- For the != operator ([expr.eq]), the rewritten candidates include all non-rewritten candidates for the expression x == y.
- For the equality operators, the rewritten candidates also include a synthesized candidate, with the order of the two parameters reversed, for each non-rewritten candidate for the expression y == x.
- For all other operators, the rewritten candidate set is empty.
And [over.match.oper]/9:
If a rewritten operator== candidate is selected by overload resolution for an operator @, its return type shall be cv bool, and x @ y is interpreted as:
- if @ is != and the selected candidate is a synthesized candidate with reversed order of parameters, !(y == x),
- otherwise, if @ is !=, !(x == y),
- otherwise (when @ is ==), y == x,
in each case using the selected rewritten operator== candidate.
As such, an explicit overload for operator!=
is no longer necessary. The removal of the operator has not changed comparison semantics.
All containers have had their operator!=
removed, as far as I can tell (check e.g. the vector synopsis). The only exceptions are the container adaptors std::queue
and std::stack
: my guess is that it is to preserve backwards compatibility when used with third-party containers, in case the equality operators are not symmetric.
Related Topics
What Is a Good Oo C++ Wrapper for SQLite
What Does the Fpermissive Flag Do
Converting Bool to Text in C++
Cleaning Up an Stl List/Vector of Pointers
Netbeans 7.2 Shows "Unable to Resolve Identifier" , Although Build Is Successful
How to Create a Simple Qt Console Application in C++
Deprecated Conversion from String Literal to 'Char*'
Embed Resources (Eg, Shader Code; Images) into Executable/Library with Cmake
Is There Actually a Reason Why Overloaded && and || Don't Short Circuit
Calling the Base Class Constructor from the Derived Class Constructor
How to Alpha Blend Rgba Unsigned Byte Color Fast
Is Rvo (Return Value Optimization) Applicable for All Objects
So Can Unique_Ptr Be Used Safely in Stl Collections
Converting Float Values from Big Endian to Little Endian
Should a Move Constructor Take a Const or Non-Const Rvalue Reference