三路运算符 <=> 返回带有隐式转换函数的结构体

康桓瑋*_*康桓瑋 7 c++ language-lawyer implicit-conversion spaceship-operator c++20

考虑以下无用的代码:

struct S{
  constexpr operator int() const { return 0; }
  constexpr auto operator<=>(S) const { return *this; }
};

static_assert(S{} <= S{});
Run Code Online (Sandbox Code Playgroud)

Clang 和 MSVC 接受此代码,但 GCC拒绝它并显示错误消息:

error: no match for 'operator<=' (operand types are 'S' and 'int')
Run Code Online (Sandbox Code Playgroud)

哪个编译器是对的?是怎么operator<=合成的operator<=>

IlC*_*ano 4

来自[over.match.oper](3.4.18):

对于关系 ([expr.rel]) 运算符,重写的候选者包括表达式 x <=> y 的所有未重写的候选者。

如果operator<=>通过 的重载决策选择重写候选者operator @,则使用所选的重写候选者x @ y将其解释为 [...] [...] 。重写的候选者不会在结果表达式的上下文中被考虑。(x <=> y) @ 0operator<=>operator @

因此,对于表达式S{} <= S{},所选运算符将为S::operator<=>(S) const,并且表达式将被重写为(S{} <=> S{}) <= 0。在重写的表达式中,操作数的类型为S和,将选择int内置操作数。operator<=(int, int)所以最终表达式(转换S为后int)将得到0 <= 0,即true

总之,Clang 和 MSVC 在这种情况下是正确的,而 GCC 似乎无法解释(S{} <=> S{}) <= 0为对内置运算符的调用(请注意错误消息读数operand types are 'S' and 'int')。如果将 中的条件更改static_assert为重写的表达式(S{} <=> S{}) <= 0,则所有三个编译器都会接受它