![](/img/trans.png)
[英]Configured CMake to compile in C++20 but the executable compiles in C++17
[英]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
。
这是因为所有的功能现在都是候选的,而在它们之前不是。
这意味着突然之间, A
的operator==
成为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; }
};
另一种不需要更改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
中的函数,但它不会被调用并且生成的代码仍然会非常令人惊讶。
但是,请注意您的代码之前已损坏。
这是在 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++ 扩展了关系运算符的重载解析以包含交换的参数,这在诊断消息中:
所以你需要更少的操作员。 事实上,您可以考虑重载/默认三向比较运算符 ( <=>
)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.