Fed*_*dor 35 c++ comparison-operators language-lawyer c++-concepts c++20
升级到最新的 Visual Studio 2022 版本 17.6 后,我们的自定义视图之一停止被识别为std::ranges::range. 事实证明,问题出在视图的迭代器中operator ==。operator !=
请找到下面的最小简化示例(已经没有视图和迭代器):
struct A {
friend bool operator ==( const A &, const A & ) = default;
};
struct B {
friend bool operator ==( const B &, const B & ) = default;
friend bool operator ==( const B &, const A & ) { return false; }
// Visual Studio 2022 version 17.6 does not like next line
friend bool operator !=( const B &, const A & ) { return true; }
};
template< class T, class U >
concept comparable =
requires(const std::remove_reference_t<T>& t,
const std::remove_reference_t<U>& u) {
{ t == u } -> std::same_as<bool>;
{ t != u } -> std::same_as<bool>;
{ u == t } -> std::same_as<bool>;
{ u != t } -> std::same_as<bool>;
};
// ok in GCC, Clang and Visual Studio before version 17.6
static_assert( comparable<A, B> );
Run Code Online (Sandbox Code Playgroud)
该示例被 GCC 和 Clang 接受,但不被最新的 Visual Studio 接受,它会打印错误:
<source>(25): error C2607: static assertion failed
<source>(25): note: the concept 'comparable<A,B>' evaluated to false
<source>(18): note: 'bool operator ==(const B &,const A &)': rewritten candidate function was excluded from overload resolution because a corresponding operator!= declared in the same scope
<source>(4): note: could be 'bool operator ==(const A &,const A &)' [found using argument-dependent lookup]
<source>(8): note: or 'bool operator ==(const B &,const B &)' [found using argument-dependent lookup]
<source>(9): note: or 'bool operator ==(const B &,const A &)' [found using argument-dependent lookup]
<source>(4): note: or 'bool operator ==(const A &,const A &)' [synthesized expression 'y == x']
<source>(8): note: or 'bool operator ==(const B &,const B &)' [synthesized expression 'y == x']
<source>(9): note: or 'bool operator ==(const B &,const A &)' [synthesized expression 'y == x']
<source>(18): note: 'bool operator ==(const B &,const A &)': rewritten candidate function was excluded from overload resolution because a corresponding operator!= declared in the same scope
<source>(18): note: while trying to match the argument list '(const A, const B)'
Run Code Online (Sandbox Code Playgroud)
在线演示: https: //gcc.godbolt.org/z/evTfofq3d
这是新的 Visual Studio 编译器中的错误,还是相反,其他编译器是错误的?
Bar*_*rry 31
这是您正在寻找的等式运算符\n( P2468 )的结果。
\nC++20 中的比较更改(允许a == b并a != b找到重写和综合候选者)可能会破坏许多 C++17 代码。你可以在论文中看到一些例子。
为了减轻这些中断(不是全部,但至少是大多数),MSVC 编译器开发人员 Cameron DaCamara \xe2\x80\xa0提出了一个狭窄的规则,尝试对 C 使用 C++17 比较规则++17 左右的代码。基本上,在 C++20 中,不需要编写,operator!=因为现有的operator==就足够了(a != b可以使用重写的表达式!(a == b),这几乎总是您想要的,所以为什么要费心编写它呢?)。但在 C++17 中,我们还没有这条规则,所以你必须同时编写这两个规则。
因此,建议的规则如下:如果我们找到 anoperator==和 anoperator!=在相同范围内且参数相同,则使用 C++17 规则。否则,请使用 C++20 规则。这是我希望没有人需要知道的 C++ 规则之一 - 您的 C++17 代码将继续正常工作,而您的新 C++20 代码将开始工作。
使用发布的简化示例:
\nstruct A { };\n\nstruct B {\n friend bool operator==(const B &, const A &);\n friend bool operator!=(const B &, const A &);\n};\n\nint main() {\n return A{} == B{};\n}\nRun Code Online (Sandbox Code Playgroud)\n在 C++17 中,这无法编译,因为没有可行的运算符。
\n在 C++20 中,在我正在讨论的论文之前,这将评估为B{} == A{}。但在 C++20 中,通过此 DR 的解决方案,因为您提供了operator==和 ,operator!=并且它们是相同的,我们假设您想要 C++17 规则,所以我们回到这个不编译,因为我们不考虑重写的候选者。
修复方法很简单,就是不提供该运算符:
\nstruct A { };\n\nstruct B {\n friend bool operator==(const B &, const A &);\n};\n\nint main() {\n return A{} == B{};\n}\nRun Code Online (Sandbox Code Playgroud)\n或者,如果您需要同时处理 C++17/C++20,那么这个还不够,因为您需要提供其他三个 - 都是有条件的:
\nstruct A { };\n\nstruct B {\n friend bool operator==(const B&, const A&);\n#if !(defined(__cpp_impl_three_way_comparison) and __cpp_impl_three_way_comparison >= 201907)\n friend bool operator!=(const B& b, const A& a) { return !(b == a); }\n friend bool operator==(const A& a, const B& b) { return b == a; }\n friend bool operator!=(const A& a, const B& b) { return !(b == a); }\n#endif\n};\n\nint main() {\n return A{} == B{};\n}\nRun Code Online (Sandbox Code Playgroud)\n作为更新,这是CWG 2804,它指出 P2468 中的具体措辞没有完全正确地涵盖意图,并且没有按照friend您可能期望的方式处理运算符。从问题来看:
struct X {\n operator int();\n friend bool operator==(X, int);\n friend bool operator!=(X, int); // #1\n} x;\n\nbool bx = x == x; // error: lookup for rewrite target determination does not find hidden friend #1\n\nstruct Y {\n operator int();\n friend bool operator==(Y, int); // #2\n} y;\n\nbool operator!=(Y, int); // #3\n\nbool by = y == y; // OK, #2 is not a rewrite target because lookup finds #3\nRun Code Online (Sandbox Code Playgroud)\n可以说,其意图是bx应该没问题(在这种情况下我们不应该考虑重写,因为operator==和operator!=是一起声明的)并且by应该是一个错误(因为在这种情况下我们应该考虑重写,并考虑重写会导致歧义)。
\xe2\x80\xa0从技术上讲,除了 Cameron 之外,本文还列出了其他 9 位作者,包括我,但 Cameron 完成了这里的大部分工作,他和 Richard Smith 提出了最终的规则集。就我对这篇论文的有意义参与而言,我是那个破坏了需要修复的代码的人。因此,考虑到 Cameron 提出了这个设计,并针对大量现有代码对其进行了测试,MSVC 成为第一个实现新规则的编译器也就不足为奇了。
\n在P2468之后,如果可以通过在相同范围内的搜索找到operator==匹配,则不会使用函数进行重写比较。operator!=
MSVC 对该功能的标准措辞的解释似乎与 GCC/Clang 不同。
y == x对于相等运算符,重写候选者还包括一个合成候选者,其中两个参数的顺序相反,对于作为具有第一个操作数 的重写目标的表达式的每个非重写候选者y。
非模板函数或
F名为的函数模板operator==是具有第一个操作数的重写目标o,除非从运算符表达式的实例化上下文中在范围S中搜索名称找到对应的函数或函数模板(如果其名称为),其中如果是类成员, S是类类型的范围,否则是成员的命名空间范围。operator!=Foperator==oFF
在范围(如[class.member.lookup]/1中定义)中搜索名称仅查找该范围([basic.lookup.general]/3)中绑定到该名称的声明。友元声明不绑定名称([dcl.meaning.general]/2.1),因此不应阻止成为重写候选者(至少根据此规则的字面解释,这似乎是 GCC 和 Clang 实现的):friend operator!=(const B&, const A&)operator==(const B&, const A&)A() == B()
struct A {};
struct B {
friend bool operator==(B, A);
friend bool operator!=(B, A);
};
bool x = A() == B(); // OK in GCC/Clang
bool operator!=(B, A);
bool y = A() == B(); // error in GCC/Clang
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4505 次 |
| 最近记录: |