[英]Three-way operator <=> return struct with implicit conversion function
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: Clang 和 MSVC 接受此代码,但 GCC拒绝它并显示错误消息:
error: no match for 'operator<=' (operand types are 'S' and 'int')
Which compiler is right?哪个编译器是对的? How
operator<=
is synthesized from operator<=>
? operator<=
如何从operator<=>
合成?
From [over.match.oper] ( 3.4.1 and 8 ):从 [over.match.oper] ( 3.4.1和8 ):
For the relational ([expr.rel]) operators, the rewritten candidates include all non-rewritten candidates for the expression x <=> y.
对于关系 ([expr.rel]) 运算符,重写的候选包括表达式 x <=> y 的所有未重写的候选。
and和
If a rewritten
operator<=>
candidate is selected by overload resolution for anoperator @
,x @ y
is interpreted as [...](x <=> y) @ 0
[...], using the selected rewrittenoperator<=>
candidate.如果通过重载决策为
operator @
选择了重写的operator<=>
候选者,则x @ y
被解释为 [...](x <=> y) @ 0
[...],使用所选的重写operator<=>
候选人。 Rewritten candidates for theoperator @
are not considered in the context of the resulting expression.在结果表达式的上下文中不考虑
operator @
的重写候选。
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
.因此,对于表达式
S{} <= S{}
,选定的运算符将是S::operator<=>(S) const
并且表达式将被重写为(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.在重写的表达式中,操作数的类型是
S
和int
,将选择内置的operator<=(int, int)
。 So ultimately the expression (after converting S
to an int
) will result in 0 <= 0
, which is true
.所以最终表达式(在将
S
转换为int
之后)将导致0 <= 0
,这是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'
).总之 Clang 和 MSVC 在这种情况下是正确的,并且 GCC 似乎无法将
(S{} <=> S{}) <= 0
解释为对内置运算符的调用(请注意读取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 .如果您将
static_assert
中的条件更改为重写后的表达式(S{} <=> S{}) <= 0
,则所有三个编译器都接受它。
C++20 support in GCC is still experimental , so while it does support the three-way operator , your static_assert
is failing because the other compilers are auto inferring the <=
operator from the <=>
operator, while GCC seems to be more pedantic in its interpretation of the standard, and since you do not have the <=
operator directly, the compiler is then emitting a compile time error because it can't find the <=
operator. GCC 中的 C++20 支持仍处于实验阶段,因此虽然它支持三向运算符,但您的
static_assert
失败,因为其他编译器自动从<=>
运算符推断<=
运算符,而 GCC 似乎更多对标准的解释很迂腐,并且由于您没有直接使用<=
运算符,因此编译器会发出编译时错误,因为它找不到<=
运算符。
If you add the <=
operator, the code works, example:如果添加
<=
运算符,则代码有效,例如:
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{});
Additionally, if you change your assert to be the three way operator, the test fails on all compilers, example:此外,如果您将断言更改为三向运算符,则测试在所有编译器上都会失败,例如:
struct S{
constexpr operator int() const { return 0; }
constexpr auto operator<=>(S) const { return *this; }
};
static_assert(S{} <=> S{});
Furthermore, since the three-way operator is expected to essentially return a negative, zero, or positive value (really returning an ordering), returning *this
is likely converting the value to something that Clang and MSVC interpret as a true
value for the assert, while GCC could be converting it to a false
value, and thus the assert fails.此外,由于预计三路运算符基本上返回负值、零或正值(实际上返回一个排序),返回
*this
可能会将值转换为 Clang 和 MSVC 解释为断言的true
值的值,而 GCC 可能会将其转换为false
值,因此断言失败。
If you change the return type to any negative value (even -0
) or a zero value, the assert passes on all compilers, additionally if you change the value to any positive value above 0 the assert fails on all compilers.如果您将返回类型更改为任何负值(甚至
-0
)或零值,则断言将传递给所有编译器,此外,如果您将值更改为任何高于 0 的正值,则断言在所有编译器上都会失败。
You could change the three-way operator to cast *this
to an int
which would call operator int
and return 0, which would then cause the assert to pass, example:您可以将三向运算符更改为将
*this
强制转换为int
,该 int 将调用operator int
并返回 0,这将导致断言通过,例如:
constexpr auto operator<=>(S) const { return static_cast<int>(*this); }
So to answer directly:所以直接回答:
Which compiler is right?
哪个编译器是对的?
In my experience with GCC, it tends to interpret the language very pedantically and err on the side of caution when it comes to a language specification that might be ambiguous in the face of an odd code snippet (like yours).根据我对 GCC 的经验,当涉及到面对奇怪的代码片段(如您的代码片段)可能模棱两可的语言规范时,它往往会非常迂腐地解释语言,并且会谨慎行事。
To that end, the other compilers might be too loose in their interpretation of the language or GCC might be too strict in this particular case.为此,其他编译器对语言的解释可能过于松散,或者 GCC 在这种特殊情况下可能过于严格。
Either way, even though this code is "useless," anyone who is running into something like this who is targeting all 3 compilers should probably try to be as pedantic as possible with this type of code, though that might necessarily defeat the purpose of the code in this case, unfortunately.无论哪种方式,即使这段代码是“无用的”,任何遇到这样的事情并针对所有 3 个编译器的人都应该尽可能地尝试使用这种类型的代码,尽管这可能必然会违背不幸的是,这种情况下的代码。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.