seh*_*ehe 107 c++ spaceship-operator c++17 c++20
我一直将其精简为仅使用Boost Operators:
#include <boost/operators.hpp>
struct F : boost::totally_ordered1<F, boost::totally_ordered2<F, int>> {
/*implicit*/ F(int t_) : t(t_) {}
bool operator==(F const& o) const { return t == o.t; }
bool operator< (F const& o) const { return t < o.t; }
private: int t;
};
int main() {
#pragma GCC diagnostic ignored "-Wunused"
F { 42 } == F{ 42 }; // OKAY
42 == F{42}; // C++17 OK, C++20 infinite recursion
F { 42 } == 42; // C++17 OK, C++20 infinite recursion
}
Run Code Online (Sandbox Code Playgroud)
该程序在 GCC 和 Clang 中使用 C++17(启用 ubsan/asan)编译并运行良好。
当您将隐式构造函数更改为 时explicit,有问题的行显然不再在 C++17 上编译
令人惊讶的是,这两个版本都在 C++20(v1和v2)上编译,但它们会导致无法在 C++17 上编译的两行上的无限递归(崩溃或紧密循环,取决于优化级别)。
显然,这种通过升级到 C++20 而潜入的无声错误令人担忧。
问题:
Bar*_*rry 83
事实上,不幸的是,C++20 使这段代码无限递归。
这是一个简化的示例:
struct F {
/*implicit*/ F(int t_) : t(t_) {}
// member: #1
bool operator==(F const& o) const { return t == o.t; }
// non-member: #2
friend bool operator==(const int& y, const F& x) { return x == y; }
private:
int t;
};
Run Code Online (Sandbox Code Playgroud)
我们来看看42 == F{42}。
在 C++17 中,我们只有一个候选:非成员候选 ( #2),所以我们选择它。它的主体 ,x == y本身只有一个候选:成员候选 ( #1) ,它涉及隐式转换y为F。然后该成员候选人比较两个整数成员,这完全没问题。
在 C++20 中,初始表达式42 == F{42}现在有两个候选项:既#2像以前一样是非成员候选项 ( ),现在还有反向成员候选项 ( #1reversed)。#2是更好的匹配 - 我们完全匹配两个参数而不是调用转换,所以它被选中。
然而,x == y现在现在有两个候选人:再次是成员候选人 ( #1),还有反向的非成员候选人 ( #2reversed )。#2再次是更好的匹配,原因与之前它是更好的匹配相同:不需要转换。所以我们y == x改为评估。无限递归。
非逆转候选人比逆转候选人更受欢迎,但只能作为决胜局。更好的转换顺序永远是第一位的。
好的,太好了,我们该如何解决?最简单的选择是完全删除非成员候选人:
struct F {
/*implicit*/ F(int t_) : t(t_) {}
bool operator==(F const& o) const { return t == o.t; }
private:
int t;
};
Run Code Online (Sandbox Code Playgroud)
42 == F{42}这里评估为F{42}.operator==(42),效果很好。
如果我们想保留非成员候选人,我们可以明确添加其反向候选人:
struct F {
/*implicit*/ F(int t_) : t(t_) {}
bool operator==(F const& o) const { return t == o.t; }
bool operator==(int i) const { return t == i; }
friend bool operator==(const int& y, const F& x) { return x == y; }
private:
int t;
};
Run Code Online (Sandbox Code Playgroud)
这使得42 == F{42}仍然选择非成员候选人,但现在x == y在正文中将优先选择成员候选人,然后进行正常的平等。
最后一个版本还可以删除非成员候选人。以下也适用于所有测试用例,无需递归(这也是我在 C++20 中编写比较的方式):
struct F {
/*implicit*/ F(int t_) : t(t_) {}
bool operator==(F const& o) const { return t == o.t; }
bool operator==(int i) const { return t == i; }
private:
int t;
};
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4526 次 |
| 最近记录: |