eas*_*000 9 c++ comparison-operators spaceship-operator c++20
#include <compare>
#include <iostream>
int main()
{
auto comp1 = 1.1 <=> 2.2;
auto comp2 = -1 <=> 1;
std::cout << typeid(comp1).name()<<"\n"<<typeid(comp2).name();
}
Run Code Online (Sandbox Code Playgroud)
输出:
结构 std::partial_ordering
结构 std::strong_ordering
我知道如果操作数具有整数类型,则运算符将返回一个 prvalue 类型std::strong_ordering。我也知道操作数是否有浮点类型,运算符会产生类型的纯右值std::partial_ordering。
但是为什么我应该使用三向比较运算符而不是双向运算符(==, !=, <, <=, >, >=)?这对我有什么好处吗?
mol*_*ilo 10
它可以 在一次操作中确定顺序。
其他运算符需要进行两次比较。
其他运营商的总结:
a == b是假的,你不知道是否a < b或a > ba != b是真的,你不知道是否a < b或a > ba < b是假的,你不知道是否a == b或a > ba > b是假的,你不知道是否a == b或a < ba <= b是真的,你不知道是否a == b或a < ba >= b是真的,你不知道是否a == b或a > b一个很好的副作用是所有其他运算符都可以根据 实现<=>,并且编译器可以为您生成它们。
另一个副作用是人们可能会对<=>数学中的等价箭头的使用感到困惑,自从打字机获得这三个符号以来,它就已经如此了。
(我个人非常的恼火如何a <=> b为“truthy”当且仅当a和b是不等价的。)
主要优点(至少对我而言)是该运算符可以默认用于类,这将自动支持类的所有可能比较。IE
#include <compare>
struct foo {
int a;
float b;
auto operator<=>(const foo& ) const = default;
};
// Now all operations used before are defined for you automatically!
auto f1(const foo& l, const foo& r) {
return l < r;
}
auto f2(const foo& l, const foo& r) {
return l > r;
}
auto f3(const foo& l, const foo& r) {
return l == r;
}
auto f4(const foo& l, const foo& r) {
return l >= r;
}
auto f5(const foo& l, const foo& r) {
return l <= r;
}
auto f6(const foo& l, const foo& r) {
return l != r;
}
Run Code Online (Sandbox Code Playgroud)
以前,所有这些操作都必须在类中定义,这既麻烦又容易出错——因为每当向类中添加新成员时,就必须记住重新访问这些操作。
使用你自己的判断。
飞船操作员的重点不是专门用于比较对象。它的要点是允许编译器从宇宙飞船运算符中合成其他比较运算符。
如果您不需要特别回答小于、等于或大于的问题,则不需要直接调用它。使用对您有意义的运算符。
但是如果你需要让一个类型具有可比性,你只需要编写 2 个函数(宇宙飞船和相等)而不是 6 个。 在编写这样一个函数时,你可以在有问题的单个类型上使用宇宙飞船运算符(如果它们是可比的)以这样的方式)。这使得实现这些功能变得更加容易。
飞船操作员允许你做的另一件事是告诉你比较将提供什么样的排序。部分,强,或其他。这在理论上可能很有用,但总体而言相当罕见。
宇宙飞船操作符是由 Herb Sutter 提出的,并被委员会采纳,用 C++ 20 实现,详细报告可以在这里查阅,或者如果你更喜欢讲座,这里你可以看到这个人自己做案例的视频它。在报告的第 3/4 页中,您可以看到主要用例:
C++20 之前版本所需的比较运算符实现位于以下类中:
class Point
{
int x;
int y;
public:
friend bool operator==(const Point &a, const Point &b) { return a.x == b.x && a.y == b.y; }
friend bool operator<(const Point &a, const Point &b) { return a.x < b.x || (a.x == b.x && a.y < b.y); }
friend bool operator!=(const Point &a, const Point &b) { return !(a == b); }
friend bool operator<=(const Point &a, const Point &b) { return !(b < a); }
friend bool operator>(const Point &a, const Point &b) { return b < a; }
friend bool operator>=(const Point& a, const Point& b) { return !(a < b); }
// ... non-comparisonfunctions ...
};
Run Code Online (Sandbox Code Playgroud)
将被替换为:
class Point
{
int x;
int y;
public:
auto operator<=>(const Point &) const = default;
// ... non-comparison functions ...
};
Run Code Online (Sandbox Code Playgroud)
因此,为了回答您的问题,重载operator<=>为类成员允许您对类对象使用所有比较运算符,而无需实现它们,operator==如果没有另外声明,则默认它也默认,这在术语中自动实现operator!=,使所有比较操作都可用一个单一的表达。此功能是主要用例。
我想指出的是,如果不引入C++20 的默认比较功能,宇宙飞船运算符就不可能实现。
默认三向比较
[...]
设R是返回类型,每对子对象a,b比较如下:
[...]
...如果R是std::strong_ordering,结果是:Run Code Online (Sandbox Code Playgroud)a == b ? R::equal : a < b ? R::less : R::greater否则,如果
R是std::weak_ordering,则结果为:Run Code Online (Sandbox Code Playgroud)a == b ? R::equivalent : a < b ? R::less : R::greater否则 (
Risstd::partial_ordering),结果为:Run Code Online (Sandbox Code Playgroud)a == b ? R::equal : a < b ? R::less : b < a ? R::greater : R::unordered根据任何重载的规则
operator<=>,默认<=>重载还允许将类型与<、<=、>和进行比较>=。如果
operator<=>是默认的并且operator==根本没有声明,那么operator==就是隐式默认的。
它还允许operator ==仅默认,这将实现operator !=,尽管不像前者那么通用,但这也是一个有趣的可能性。