嵌套生成具有三向运算符的比较运算符?

Nested generation of comparison operator with three-way operator?

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

#include <compare>

struct S {};

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

如果我将对象 Sint 进行比较,#1 会为我生成正确的运算符,因此像 S{} <= 0、[=19= 这样的表达式] 或 0 <=> S{} 只是 fine.

但是如果我将对象 S 与其他对象 S 进行比较:

S{} < S{};

那么这将被改写为(S{} <=> S{}) < 0。由于 (S{} <=> S{}) 将 return 另一个 S,我们回到问题的原点:Sint 进行比较。目前,我们没有 operator<(S, int),因此 #1 会为我生成正确的运算符。

但令人惊讶的是,三个编译器中的 none 对我这样做。 GCC、Clang 和 MSVC all 拒绝 S{} < S{} 并显示相同的错误消息:

no match for 'operator<' (operand types are 'S' and 'int')

这让我很沮丧。由于 #1 实际上存在。为什么这里没有出现嵌套的运算符生成?标准怎么说?是否存在静态约束违规?

这是错误的格式,尽管不可否认错误消息非常混乱。

来自 [over.match.oper]/8 的规则是(强调我的):

If a rewritten operator<=> candidate is selected by overload resolution for an operator @, x @ y is interpreted as 0 @ (y <=> x) if the selected candidate is a synthesized candidate with reversed order of parameters, or (x <=> y) @ 0 otherwise, using the selected rewritten operator<=> candidate. Rewritten candidates for the operator @ are not considered in the context of the resulting expression.

表达式 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 中提交了一个请求)。