提问者:小点点

用三向算子嵌套生成比较算子?


考虑以下两个用于s的重载运算符<=>:

#include <compare>

struct S {};

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

如果我将对象Sint进行比较,#1将为我生成正确的运算符,所以像S{}<=000<=>S{}这样的表达式就可以了。

但如果我将一个对象s与其他对象s:

S{} < S{};

则将重写为(S{}<=>S{})<0。由于(S{}<=>S{})将返回另一个S,所以我们回到原始问题:Sint进行比较。此时,我们没有运算符<(S,int),因此#1将为我生成正确的运算符。

但令人惊讶的是,三个编译器都没有这样对我。GCC、Clang和MSVC都拒绝S{}并带有相同的错误消息:

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

这让我很沮丧。因为#1实际存在。为什么不在这里产生运算符的嵌套生成?标准是怎么说的?是否存在静态约束冲突?


共1个答案

匿名用户

这是错误的,尽管错误消息是相当混乱的。

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

如果通过重载解析为运算符@选择了重写的运算符<=>候选,则如果选定的候选是参数顺序相反的合成候选,则x@y被解释为0@(y<=>x);否则,则使用选定的重写的运算符<=>候选。在结果表达式的上下文中不考虑操作符@的重写候选项。

表达式S{}将解析为重写的候选(S{}<=>S{})<0。结果表达式将不会在其查找中考虑重写的候选项。因此,当我们执行s{}<0时,只需要查找一个运算符<,而不是运算符<=>。它找不到这样的东西,所以表达方式是畸形的。

<source>:8:14: error: no match for 'operator<' (operand types are 'S' and 'int')
    8 | auto x = S{} < S{};
      |          ~~~~^~~~~

在这个意义上,错误是真的:没有匹配,特别是operator<与这些操作数。尽管如果错误消息中有更多的上下文来解释为什么要查找这些信息,这将会有所帮助。