提问者:小点点

重载运算符==with'


考虑具有两个运算符==重载相同structS

struct S {
  bool operator==(const S&) && { 
    return true;
  }
  bool operator==(const S&) const && { 
    return true;
  }
};

如果我将两个S运算符==进行比较:

S{} == S{};

gcc和msvc接受此代码,clang拒绝它:

<source>:14:7: error: use of overloaded operator '==' is ambiguous (with operand types 'S' and 'S')
  S{} == S{};
  ~~~ ^  ~~~

为什么clang认为这里有一个模棱两可的重载解决方案?在这种情况下,非const不是应该是最好的候选者吗?

同样,如果我将两个S与合成的运算符进行比较!=

S{} != S{};

gcc仍然接受此代码,但msvc和clang不接受:

<source>:14:7: error: use of overloaded operator '!=' is ambiguous (with operand types 'S' and 'S')
  S{} != S{};
  ~~~ ^  ~~~

合成的运算符似乎很奇怪!=突然导致msvc的歧义。哪个编译器是正确的?


共1个答案

匿名用户

这个例子在C 17中是明确的。C 20带来了变化:

[over. match.oper]

对于具有cv1 T1类型操作数的一元操作符@和具有cv1 T1类型左操作数和cv2 T2类型右操作数的二元操作符@,四组候选函数,指定成员候选函数,非成员候选函数,内置候选函数和重写候选函数,构造如下:

  • 对于操作符,一元操作符

重写后的候选集确定如下:

  • 对于相等运算符,重写候选还包括一个合成候选,对于表达式y==x的每个非重写候选,两个参数的顺序相反。

因此,重写的候选集包括以下内容:

 implicit object parameter
 |||
(S&&, const S&);       // 1
(const S&&, const S&); // 2

// candidates that match with reversed arguments
(const S&, S&&);       // 1 reversed
(const S&, const S&&); // 2 reversed

重载1比2更匹配,但是1的合成反向重载与原始非反向重载是不明确的,因为两者都将const转换为一个参数。请注意,即使重载2不存在,这实际上也是不明确的。

因此,Clang是正确的。

这也包含在信息兼容性附件中:

受影响的子句:[over. match.oper]更改:平等和不平等表达式现在可以找到颠倒和重写的候选项。

理由:通过三向比较提高相等的一致性,并更容易编写相等操作的完整补码。

对原始特征的影响:两个不同类型的对象之间的相等和不等式表达式,其中一个可以转换为另一个,可能会调用不同的运算符。同一类型的两个对象之间的相等和不等式表达式可能会变得模糊。

struct A {
  operator int() const;
};

bool operator==(A, int);        // #1
// #2 is built-in candidate: bool operator==(int, int);
// #3 is built-in candidate: bool operator!=(int, int);

int check(A x, A y) {
  return (x == y) +             // ill-formed; previously well-formed
    (10 == x) +                 // calls #1, previously selected #2
    (10 != x);                  // calls #1, previously selected #3
}