1

此代码段

#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!=() 代码在 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. _

4

3 回答 3

3

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

这意味着突然间,operator==inA成为a_1 != a. 现在允许编译器反转参数,更改a == b!(a != b),反之亦然,甚至将顺序更改为b == a.

该代码导致了一个模棱两可的调用,因为bool operator==(const A&);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; }
};

另一种不需要更改的解决方案A,或者A_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,但它不会被调用,并且生成的代码仍然会非常令人惊讶。


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

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

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 将只接受更少的具有令人惊讶行为的代码。

于 2021-11-16T14:11:38.300 回答
0

去掉就好了A::operator==

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

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

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

于 2021-11-16T14:09:48.033 回答
0

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

  • GCC 标记候选人“(反转)”和
  • Clang 提及(参数顺序相反)

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

于 2021-11-16T14:11:46.607 回答