Consider the following useless code:
struct S{
  constexpr operator int() const { return 0; }
  constexpr auto operator<=>(S) const { return *this; }
};
static_assert(S{} <= S{});
Clang and MSVC accept this code but GCC rejects it with an error message:
error: no match for 'operator<=' (operand types are 'S' and 'int')
Which compiler is right? How operator<= is synthesized from operator<=>?
From [over.match.oper] (3.4.1 and 8):
For the relational ([expr.rel]) operators, the rewritten candidates include all non-rewritten candidates for the expression x <=> y.
and
If a rewritten
operator<=>candidate is selected by overload resolution for anoperator @,x @ yis interpreted as [...](x <=> y) @ 0[...], using the selected rewrittenoperator<=>candidate. Rewritten candidates for theoperator @are not considered in the context of the resulting expression.
So for the expression S{} <= S{} the selected operator will be S::operator<=>(S) const and the expression will be rewritten as (S{} <=> S{}) <= 0.  In the rewritten expression the types of the operands are S and int, for which the built-in operator<=(int, int) will be selected.  So ultimately the expression (after converting S to an int) will result in 0 <= 0, which is true.
In conclusion Clang and MSVC are right in this case, and GCC seems to fail in interpreting (S{} <=> S{}) <= 0 as a call to the built-in operator (notice the error message reading operand types are 'S' and 'int').  If you change the condition in the static_assert to be the rewritten expression (S{} <=> S{}) <= 0, then all three compilers accept it.
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