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

标签 c++ operator-overloading comparison language-lawyer 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 rejects它与:
<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 doesn't :
<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]

For a unary operator @ with an operand of type cv1 T1, and for a binary operator @ with a left operand of type cv1 T1 and a right operand of type cv2 T2, four sets of candidate functions, designated member candidates, non-member candidates, built-in candidates, and rewritten candidates, are constructed as follows:

  • ...
  • For the operator ,, the unary operator &, or the operator ->, the built-in candidates set is empty. For all other operators, the built-in candidates include all of the candidate operator functions defined in [over.built] that, compared to the given operator,
    • have the same operator name, and
    • accept the same number of operands, and
    • accept operand types to which the given operand or operands can be converted according to [over.best.ics], and
    • do not have the same parameter-type-list as any non-member candidate that is not a function template specialization.

The rewritten candidate set is determined as follows:

  • ...
  • For the equality operators, the rewritten candidates also include a synthesized candidate, with the order of the two parameters reversed, for each non-rewritten candidate for the expression 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 是正确的。

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

Affected subclause: [over.match.oper] Change: Equality and inequality expressions can now find reversed and rewritten candidates.

Rationale: Improve consistency of equality with three-way comparison and make it easier to write the full complement of equality operations.

Effect on original feature: Equality and inequality expressions between two objects of different types, where one is convertible to the other, could invoke a different operator. Equality and inequality expressions between two objects of the same type could become ambiguous.

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
}

关于c++ - 使用 `&&` 和 `const` 限定符重载 operator== 会导致 C++20 中的歧义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66761331/

相关文章:

c++ - 不正确的字符串比较

c# - C#中的文件比较

带有嵌套模板的 C++ typedef 不是类、结构或 union 类型

c++ - 我们可以为模板特化引入别名吗?

c++ - & 运算符在运算符重载中有什么作用?

c++ - 如何重载 = 运算符以根据左值类型控制行为

php - PHP 中相当于 Ruby 的 `__END__` 或 Perl 的 `__DATA__` 的构造

c++ - 如何根据输入值调用函数?

C++ ffmpeg x264编码设置

C++ istream operator>> 坏数据处理