用三向运算符嵌套生成比较运算符?
考虑以下两个过载operator<=>为S:
#include <compare>
struct S {};
int operator<=>(S, int) { return 0; } #1
S operator<=>(S, S) { return {}; } #2
如果我比较的对象S有int,在#1将产生正确的运营商对我来说,这么喜欢表达S{} <= 0,0 < S{}或0 <=> S{}将只是罚款。
但是,如果我将一个对象S与其他对象进行比较S:
S{} < S{};
那么这将被重写为(S{} <=> S{}) < 0. 既然(S{} <=> S{})会返回 other S,我们回到原点问题:S与 a 进行比较int。目前,我们没有operator<(S, int),因此#1将为我生成正确的运算符。
但令人惊讶的是,三个编译器都没有对我这样做。GCC、Clang 和 MSVC都拒绝S{} < S{}并显示相同的错误消息:
no match for 'operator<' (operand types are 'S' and 'int')
这让我很沮丧。既然#1确实存在。为什么这里没有发生运算符的嵌套生成?标准怎么说?是否存在静态约束违规?
回答
这是格式错误的,尽管不可否认,错误消息非常令人困惑。
来自[over.match.oper]/8的规则是(强调我的):
如果
operator<=>通过重载决议为运算符选择了重写候选者@,则将其x @ y解释为0 @ (y <=> x)所选候选者是具有相反参数顺序的合成候选者,或者(x <=> y) @ 0使用所选的重写operator<=>候选者。在结果表达式的上下文中不考虑运算符的重写候选@。
该表达式S{} < S{}将解析为重写的候选人(S{} <=> S{}) < 0。结果表达式不会在其查找中考虑重写的候选者。所以,当我们这样做S{} < 0,就是要去找只是一个operator<,而不是也operator<=>。它找不到这样的东西,所以表达式是格式错误的。
<source>:8:14: error: no match for 'operator<' (operand types are 'S' and 'int')
8 | auto x = S{} < S{};
| ~~~~^~~~~
从这个意义上说,这个错误确实是正确的:没有匹配,特别 operator<是那些操作数。虽然如果错误消息有更多的上下文来解释它为什么要寻找它会有所帮助(并且我在99629 中提交了一个请求)。