繁体   English   中英

operator==() 代码编译 w/C++17 但不编译 C++20

[英]operator==() code compiles w/C++17 but not C++20

这个代码片段

#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==() 和 operator!=() code failed 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 &)'

是的,我知道C++20 会合成operator!=之类的……但是现有的 C++17 代码不应该仍然用 C++20 编译吗?

如何解决问题,以便相同的代码同时使用 C++17 和 C++20 进行编译生成相同的结果?

对派生类进行更改可能很困难,因为该代码可能位于其他地方(这实际上是库代码); 这将是喜欢以更改Base

这是因为所有的功能现在都是候选的,而在它们之前不是。

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

由于bool operator==(const A&);代码导致不明确的调用bool operator==(const A&); in A支持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 只会接受更少的具有令人惊讶的行为的代码。

它的罚款,如果你删除A::operator==

A::operator==真的没有意义,因为模板说一切都等于Base

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

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

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

  • GCC 将候选标记为“(反向)”和
  • Clang 提及(使用反向参数顺序)

所以你需要更少的操作员。 事实上,您可以考虑重载/默认三向比较运算符 ( <=> )。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM