The following code calls operator <=> twice, with arguments reversed. But why?
GCC 10.2 and clang 12 both seem to be using libstdc++-10, whose <tuple> does provide operator <=>, so it doesn’t appear to be a case of missing standard library support and my code has to be incorrect. How to fix it?
#include <tuple>
#include <compare>
#include <iostream>
struct X {
int i;
auto operator <=>(X const& other) const {
std::cout << this << " <=> " << &other << std::endl;
return i <=> other.i;
}
};
int main() {
std::tuple{X{42}} <=> std::tuple{X{42}};
}
Short answer: You need to define operator==
for X
.
std::tuple
compares elements via a synthesized three-way comparison that uses <=>
only if the type satisfies std::three_way_comparable_with<T,U>
. Ultimately, this requires std::three_way_comparable<X>
, which requires an expositionary weakly-equality-comparable-with
concept. As you might guess, this requires ==
to be valid.
The fix is a one-liner:
bool operator==(X const& other) const = default;
Now why is ==
required when <=>
seems to do the job on its own here? I can only speculate, but it might be due to concepts being more "complete" than we're used to with only needing operator<
for example. If a type is comparable with <=>
, it should really also support equality.
As for why <=>
does not cover ==
on its own unless defaulted, this is because of the performance pitfall for classes whose equality can short-circuit (such as vectors and strings) as well as any classes that contain such types. No indication would be given that equality compares every element instead of short-circuiting, so <=>
does not handle equality unless it can guarantee that you'll avoid that pitfall (via defaulting <=>
).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With