用`&&`和`const`限定符重载operator==导致C++20中的歧义

考虑struct S具有operator==相同&&限定符和不同const限定符的两个重载:

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

如果我将两者Soperator==

S{} == S{};

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

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

为什么 clang 认为这里有一个不明确的重载决议?在这种情况下,非常量不应该是最好的候选人吗?

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

S{} != S{};

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

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

合成operator!=突然导致msvc的歧义似乎很奇怪。哪个编译器是对的?

回答

该示例在 C++17 中是明确的。C++20 带来了变化:

[over.match.oper]

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

  • ...
  • 对于运算符 , 、一元运算符 & 或运算符 -> ,内置候选集为空。对于所有其他运算符,内置候选包括 [over.built] 中定义的所有候选运算符函数,与给定运算符相比,
    • 具有相同的运营商名称,并且
    • 接受相同数量的操作数,并且
    • 接受可以根据 [over.best.ics] 将给定的一个或多个操作数转换为的操作数类型,以及
    • 不具有与任何非函数模板特化的非成员候选者相同的参数类型列表。

重写的候选集确定如下:

  • ...
  • 对于等式运算符,对于表达式 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
}

以上是用`&amp;&amp;`和`const`限定符重载operator==导致C++20中的歧义的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>