GCC 中的 C++20 支持仍处于试验阶段,因此虽然它确实支持三向运算符,但您static_assert
的失败是因为其他编译器会自动<=
从运算符推断运算<=>
符,而 GCC 似乎在解释标准,并且由于您没有<=
直接使用运算符,因此编译器会发出编译时错误,因为它找不到<=
运算符。
如果添加<=
运算符,则代码有效,例如:
struct S{
constexpr operator int() const { return 0; }
constexpr auto operator<=>(S) const { return *this; }
constexpr bool operator<=(S) { return true; }
};
static_assert(S{} <= S{});
此外,如果您将断言更改为三向运算符,则测试在所有编译器上都会失败,例如:
struct S{
constexpr operator int() const { return 0; }
constexpr auto operator<=>(S) const { return *this; }
};
static_assert(S{} <=> S{});
此外,由于预计三向运算符本质上会返回负值、零或正值(实际上是返回排序),因此返回*this
可能会将值转换为 Clang 和 MSVC 解释为true
断言的值,而 GCC可能会将其转换为一个false
值,因此断言失败。
如果您将返回类型更改为任何负值(偶数-0
)或零值,则断言将传递给所有编译器,此外,如果您将值更改为任何高于 0 的正值,则断言在所有编译器上都会失败。
您可以将三路运算符更改为*this
将int
调用operator int
并返回 0 的类型,这将导致断言通过,例如:
constexpr auto operator<=>(S) const { return static_cast<int>(*this); }
所以直接回答:
哪个编译器是对的?
根据我使用 GCC 的经验,当涉及到面对奇怪的代码片段(如您的代码片段)可能模棱两可的语言规范时,它往往会非常迂腐地解释该语言,并且在谨慎方面犯错。
为此,其他编译器可能对语言的解释过于松散,或者 GCC在这种特殊情况下可能过于严格。
无论哪种方式,即使这段代码是“无用的”,任何遇到这样的事情并针对所有 3 个编译器的人都应该尽可能地尝试使用这种类型的代码,尽管这可能必然会违背不幸的是,这种情况下的代码。