简体   繁体   English

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

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

This code snippet这个代码片段

#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;
}

Compiles without errors in C++17 (Visual Studio 2022).在 C++17 (Visual Studio 2022) 中编译没有错误。 (See C++17 operator==() and operator!=() code fails with C++20 for a more detailed example; note in that case the code compiles .) (有关更详细的示例,请参阅C++17 operator==() 和 operator!=() code failed with C++20 ;注意在这种情况下代码编译。)

Trying to build the same code with C++20 generates three compiler errors:尝试使用 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 &)'

Yes, I understand that C++20 will synthesize operator!= and the like ... but shouldn't existing C++17 code still compile with C++20?是的,我知道C++20 会合成operator!=之类的……但是现有的 C++17 代码不应该仍然用 C++20 编译吗?

How can I fix things so that the same code compiles with both C++17 and C++20 and generates identical results?如何解决问题,以便相同的代码同时使用 C++17 和 C++20 进行编译生成相同的结果?

Making changes to derived class could be difficult as that code could be elsewhere (this is actually library code);对派生类进行更改可能很困难,因为该代码可能位于其他地方(这实际上是库代码); it would be much preferred to make changes to Base .这将是喜欢以更改Base

This is because all of the functions are now candidate, and before they weren't.这是因为所有的功能现在都是候选的,而在它们之前不是。

This means that suddently, the operator== in A is a candidate for a_1 != a .这意味着突然之间, Aoperator==成为a_1 != a的候选者。 The compiler is now allowed to invert the arguments, change a == b to !(a != b) and vice versa, and even change the order to b == a .现在允许编译器反转参数,将a == b更改为!(a != b) ,反之亦然,甚至可以将顺序更改为b == a

The code result in an ambiguous call since bool operator==(const A&);由于bool operator==(const A&);代码导致不明确的调用bool operator==(const A&); in A supports a_1 != a , by inverting the operation to !(a_1 == a) and changing parameter order to finally have !(a == a_1) which is a candidate. in A支持a_1 != a ,通过将操作反转为!(a_1 == a)并更改参数顺序以最终获得!(a == a_1)作为候选对象。

There are multiple solution to this.对此有多种解决方案。

One is to simply make one candidate better by inheriting the function:一种是通过继承函数简单地使一个候选人变得更好:

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

The other would be to restraint the operator to work only with A :另一种是限制操作员只与A一起工作:

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

Another solution not requirering changing A or A_1 would be to add an overload that is always an ideal candidate inside Base as a friend function.另一种不需要更改AA_1解决方案是添加一个重载,该重载始终是Base的理想候选对象作为友元函数。

The whole code becomes this in C++20:整个代码在 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 {};

You can remove the template function in the global namespace, and you can also remove the function in the derived classes too.您可以删除全局命名空间中的模板函数,也可以删除派生类中的函数。

You are not required to remove the function in A , but it won't be call and the resulting code will be quite surprising still.您不需要删除A中的函数,但它不会被调用并且生成的代码仍然会非常令人惊讶。


However, note that your code was broken before.但是,请注意您的代码之前已损坏。

This is the compiler output for code using the operator== in C++17:这是在 C++17 中使用operator==代码的编译器输出:

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

    return EXIT_SUCCESS;
}

Compiler output:编译器输出:

<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 will just accept less code that has a surprising behaviour. C++20 只会接受更少的具有令人惊讶的行为的代码。

It's fine if you remove A::operator== .它的罚款,如果你删除A::operator==

There's really no point to A::operator== , as the template is saying that everything is equal to a Base . A::operator==真的没有意义,因为模板说一切都等于Base

but shouldn't existing C++17 code still compile with C++20?但是现有的 C++17 代码不应该仍然用 C++20 编译吗?

The committee works hard to minimise breaking changes, but they don't promise there will be none .委员会努力尽量减少破坏性更改,但他们不保证不会有任何更改。 In this case it was felt that having == be an equivalence relation was more important than maintaining existing behaviour.在这种情况下,人们认为让==成为等价关系比保持现有行为更重要。 As the linked question notes, polymorphic equality testing is often a source of bugs.正如链接的问题所指出的,多态相等性测试通常是错误的来源。

C++ extends overload resolution for relational operator to include swapped arguments, which is in the diagnostic message: C++ 扩展了关系运算符的重载解析以包含交换的参数,这在诊断消息中:

  • GCC marks the candidate "(reversed)" and GCC 将候选标记为“(反向)”和
  • Clang mentions (with reverser paramater order) Clang 提及(使用反向参数顺序)

So you need fewer operators.所以你需要更少的操作员。 In fact, you could consider overloading / defaulting the three-way comparison operator ( <=> ) instead.事实上,您可以考虑重载/默认三向比较运算符 ( <=> )。

暂无
暂无

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

相关问题 将 CMake 配置为在 C++20 中编译,但可执行文件在 C++17 中编译 - Configured CMake to compile in C++20 but the executable compiles in C++17 如果无法编译未使用的函数,则模板类可以使用 C++17 编译,但不能使用 C++20 编译 - Template class compiles with C++17 but not with C++20 if unused function can't be compiled c++17 (c++20) 无序 map 和自定义分配器 - c++17 (c++20) unordered map and custom allocator 在 c++17 中编译的简单代码在 c++20 中产生错误 - simple code that compile in c++17 produce error with c++20 在现代 C++11 / C++14 / C++17 和未来的 C++20 中枚举到字符串 - enum to string in modern C++11 / C++14 / C++17 and future C++20 为什么`std :: reference_wrapper`在c ++ 17中被弃用,而在c ++ 20中被删除? - Why is `std::reference_wrapper` deprecated in c++17 and removed in c++20? 为什么 rebind <U>::other 在 C++17 中被弃用并在 C++20 中被删除?</u> - Why rebind<U>::other are deprecated in C++17 and removed in C++20? 在 C++17 和 C++20 之间可移植地使用 UTF-8 字符串文字前缀 - Using UTF-8 string-literal prefixes portably between C++17 and C++20 模板友元 function 中的 C++17 和 C++20 与一元和二元运算符的区别 - Difference of C++17 and C++20 in template friend function with unary and binary operators 在 C++17 / C++20 中从 wstring 转换为 u16string 并返回(符合标准) - Conversion from wstring to u16string and back (standard conform) in C++17 / C++20
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM