6

考虑以下两个重载operator<=>S

#include <compare>

struct S {};

int operator<=>(S, int) { return 0;  } #1
S   operator<=>(S,   S) { return {}; } #2

如果我将一个对象S与 进行比较int,则会为我生成正确的运算符,所以像, or#1这样的表达式就可以S{} <= 00 < 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实际存在。为什么这里没有发生操作符的嵌套生成?标准是怎么说的?是否存在静态约束违规?

4

1 回答 1

8

这是格式错误的,尽管错误消息确实令人困惑。

来自[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中提交了一个请求),这会有所帮助。

于 2021-03-17T14:04:56.623 回答