Why does operator== only get generated for *defaulted* operator<=>?
To elaborate on the title, consider a defaulted three-way comparison operator for a type:
``` struct MyType { auto operator<=>(const MyType& other) const = default; int value; };
int main() { MyType a{ 42 }, b{ 24 }; return a == b; // works fine } ```
Now if we change that three-way comparison operator to no longer be defaulted, but still have the same effect:
``` struct MyType { auto operator<=>(const MyType& other) const { return value <=> other.value; } int value; };
int main() { MyType a{ 42 }, b{ 24 }; return a == b; // no longer works } ```
Godbolt: https://godbolt.org/z/uyqRo6
It is easy (but annoying) to work around by defining bool operator==(const MyType& other) const { return std::is_eq(*this <=> other); }, but I feel like I shouldn't have to do that.
Is there some logical reason why the equality operators can never be generated from a non-defaulted three-way comparison operator?
I'm already aware of the potential performance losses with three-way comparisons on complex types, but for simple struct types I feel like this should be automatic and just as performant.
18 comments · 83% upvoted · other discussions
Because building == from <=> can result in a less performant == than building == directly. (Yet building <, <=, etc from <=> is not typically slower.)
For example, a performant == on a container would first check whether the sizes were equal, before checking the elements. But <=> needs to check the elements to determine which is less.
In general, <=> returns more information than ==, so it may need to do more work.
The real question is why does <=> generate == when defaulted, instead of keeping them completely separate always.
rasm866i 3 points · May 2020
I suppose the point is that when you give the compiler the freedom to generate the lexicographical compare, it also knows how to efficiently generate the equality operators, and there it no reason to not let it do that. If you define the semantics of <=> to be lexicographical compare, then it SHOULD be a hassle to get different semantics for == and !=. This is different when you write it yourself, because then you define non-standard semantics on comparisons, which the compiler has no idea how to translate efficiently to equality.
I do have a question here tho: If a MyType defined above had a user type with a userdefined <=> but no ==, would defaulting <=> on MyType generate == and !=, even though it does not know how to do that efficiently?
tvaneerd 1 points · May 2020 · C++ Committee, lockfree, PostModernCpp
hmmm, don't know what it would do in that case (someone should try it in godbolt). Either it doesn't compile, or it generates <=> without generating ==. But MyType would be a bad type anyhow, so I'd be OK with whatever happens.
rasm866i 5 points · May 2020
https://godbolt.org/z/Fo-JTy
Turns out, it does the sensible thing:
doesn't try to auto-generate possibly sub-optimal == and != when those are not available from the member types.
Who would have guessed that the c++ committee makes sensible choises? :D
Pazer2 [OP] 1 points · May 2020
Why would MyType be a bad type?