【问题标题】:operator==() code compiles w/C++17 but not C++20operator==() 代码编译 w/C++17 但不是 C++20
【发布时间】:2021-12-27 14:35:44
【问题描述】:

这段代码sn-p

#include <stdlib.h>

struct Base { };
template<typename T>
inline bool operator==(const T&, const Base&)
{
    return true;
}
template<typename T>
inline bool operator!=(const T& lhs, const Base& rhs)
{
    return !(lhs == rhs);
}

struct A : public Base
{
    bool operator==(const A&) const { return true; }
};

struct A_1 final : public A { };

int main()
{
    const A a;
    const A_1 a_1;
    if (a_1 != a) {}

    return EXIT_SUCCESS;
}

在 C++17 (Visual Studio 2022) 中编译没有错误。 (有关更详细的示例,请参阅 C++17 operator==() and operator!=() code fails with C++20;请注意在这种情况下代码编译。)

尝试使用 C++20 构建相同的代码会产生三个编译器错误:

error C2666: 'operator !=': 3 overloads have similar conversions
message : could be 'bool A::operator ==(const A &) const' [rewritten expression '!(x == y)']
message : or 'bool A::operator ==(const A &) const' [synthesized expression '!(y == x)']
message : or       'bool operator !=<A_1>(const T &,const Base &)'

是的,我 understand 认为 C++20 将合成 operator!= 等......但是现有的 C++17 代码不应该仍然用 C++20 编译吗?我该如何解决问题,以使 same 代码同时使用 C++17 和 C++20 编译 并且 生成相同的结果?

对派生类进行更改可能很困难,因为该代码可能在其他地方(这实际上是库代码); 非常更愿意更改Base

【问题讨论】:

  • c++20 如果不存在,则从 operator == 生成 operator !=。所以也提供所有operator !=
  • "但现有的 C++17 代码不应该仍然使用 C++20 编译" - 不。 C++20 引入了操作数重新排序。这是一个已知的潜在重大变化。
  • 这个问题和你之前的问题C++17 operator==() and operator!=() code fails with C++20有什么不同?
  • @CrisLuengo "...注意在这种情况下代码编译。"
  • 代码略有不同,但问题是一样的,我想。

标签: c++ c++17 c++20 spaceship-operator


【解决方案1】:

这是因为所有函数现在都是候选函数,而之前它们不是。

这意味着突然间,A 中的operator== 成为a_1 != a 的候选对象。现在允许编译器反转参数,将a == b 更改为!(a != b),反之亦然,甚至将顺序更改为b == a

由于A 中的bool operator==(const A&amp;); 支持a_1 != a,代码导致模棱两可的调用,方法是将操作反转为!(a_1 == a) 并更改参数顺序以最终获得!(a == a_1),这是一个候选对象。

对此有多种解决方案。

一种是通过继承函数来简单地使一个候选人变得更好:

struct A_1 final : public A { using A::operator==; };

另一个是限制操作员只使用A

struct A : public Base
{
    friend bool operator==(std::same_as<A> auto const&, std::same_as<A> auto const&) { return true; }
};

另一个不需要更改 AA_1 的解决方案是在 Base 中添加一个始终是理想候选者的重载作为友元函数。

整个代码在 C++20 中变成了这样:

struct Base {
    friend bool operator==(std::derived_from<Base> auto const&, std::derived_from<Base> auto const&) { return true; }
};

struct A : Base {};
struct A_1 final : A {};

您可以在全局命名空间中删除模板函数,也可以在派生类中删除该函数。

您不需要删除A 中的函数,但它不会被调用,生成的代码仍然会非常令人惊讶。


但是,请注意您的代码之前已损坏。

这是在 C++17 中使用 operator== 的代码的编译器输出:

int main()
{
    const A a;
    const A_1 a_1;
    if (!(a_1 == a)) {}

    return EXIT_SUCCESS;
}

编译器输出:

<source>: In function 'int main()':
<source>:26:15: error: ambiguous overload for 'operator==' (operand types are 'const A_1' and 'const A')
   26 |     if (!(a_1 == a)) {}
      |           ~~~ ^~ ~
      |           |      |
      |           |      const A
      |           const A_1
<source>:17:10: note: candidate: 'bool A::operator==(const A&) const'
   17 |     bool operator==(const A&) const { return true; }
      |          ^~~~~~~~
<source>:5:13: note: candidate: 'bool operator==(const T&, const Base&) [with T = A_1]'
    5 | inline bool operator==(const T&, const Base&)
      |             ^~~~~~~~
ASM generation compiler returned: 1

C++20 将只接受更少的具有令人惊讶行为的代码。

【讨论】:

  • 最好在Base 级别做一些事情,这样我就不必翻遍所有派生类......甚至可能很难找到 所有这些。
  • 没有来自 Visual Studio 2022 的警告 :-(
  • @JackBrown 我的std::same_as 解决方案对您有用吗?它不需要更改派生类。
  • 这个答案中的same_as 方法很有希望。如果您试一试并且效果很好,我会对您在接受答案之外可能获得的其他反馈感兴趣。
  • @JackBrown 我添加了一个不需要更改AA_1 的解决方案,并且只需要更改Base
【解决方案2】:

It's fine 如果您删除 A::operator==

A::operator== 确实没有意义,因为模板说 所有内容 都等于 Base

但现有的 C++17 代码不应该仍然用 C++20 编译吗?

委员会努力尽量减少重大更改,但他们不保证不会有没有。在这种情况下,人们认为让== 成为等价关系比保持现有行为更重要。正如链接的问题所指出的,多态相等性测试通常是错误的来源。

【讨论】:

  • 它是现有的代码......并缩减为一个minimal reproducible example 用于 SO。就这个问题而言,问题是现有代码用 C++17 编译,而不是 C++20。
  • @JackBrown 是的,因为 C++20 与 C++17 相比发生了重大变化。没有人承诺没有
【解决方案3】:

C++ 扩展了关系运算符的重载解析以包括交换参数,这在诊断消息中:

  • GCC 将候选标记为“(反转)”并且
  • Clang 提及(参数顺序颠倒)

因此您需要更少的运算符。事实上,您可以考虑重载/默认三路比较运算符 (&lt;=&gt;)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-06-29
    • 2021-12-06
    • 2019-10-15
    • 2022-07-06
    • 2018-07-04
    • 1970-01-01
    • 2011-06-14
    相关资源
    最近更新 更多