为什么要使用三向比较运算符 (<=>) 而不是双向比较运算符?这有优势吗?
Why should I use the three-way comparison operator (<=>) instead of the two-way comparison operators? Does this have an advantage?
#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();
}
输出:
struct std::partial_ordering
struct std::strong_ordering
我知道如果操作数是整数类型,运算符returns一个PRvalue类型std::strong_ordering
。我也知道操作数是否具有浮点类型,运算符会产生类型为 std::partial_ordering
.
的 PRvalue
但是为什么我要使用三向比较运算符而不是双向比较运算符(==
, !=
, <
, <=
, >
, >=
)?这对我有什么好处吗?
主要优点(至少对我而言)是这个运算符可以默认为 class,这将自动支持您的 class 所有可能的比较。即
#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;
}
以前,所有这些操作都必须在 class 中定义,这很麻烦且容易出错 - 因为每当新成员添加到 [=15] 时,都必须记住重新访问这些操作=].
可以在一次操作中确定顺序。
其他运算符需要进行两次比较。
其他算子总结:
- 如果
a == b
为假,你不知道是a < b
还是a > b
- 如果
a != b
为真,你不知道是a < b
还是a > b
- 如果
a < b
为假,你不知道是a == b
还是a > b
- 如果
a > b
为假,你不知道是a == b
还是a < b
- 如果
a <= b
为真,你不知道是a == b
还是a < b
- 如果
a >= b
为真,你不知道是a == b
还是a > b
一个巧妙的副作用是所有其他运算符都可以根据 <=>
实现,并且编译器可以为您生成它们。
另一个副作用是,人们可能会对使用 <=>
作为数学中的等价箭头感到困惑,自打字机使用这三个符号以来,这几乎一直存在。
(当且仅当 a
和 b
是 不 等价时,我个人对 a <=> b
是“真实的”感到非常恼火。)
请自行判断。
飞船算子的重点是不是专门比较对象的。它的要点是允许编译器从 spaceship 运算符合成 other 比较运算符。
如果您不需要特别回答小于、等于或大于的问题,则无需直接调用它。使用对您有意义的运算符。
但是如果您需要使一个类型具有可比性,您只需编写 2 个函数(spaceship 和 equality)而不是 6 个。并且在编写这样的函数时,您可以在相关的各个类型上使用 spaceship 运算符(他们应该以这种方式进行比较)。这使得实现此类功能变得更加容易。
宇宙飞船操作员允许您做的另一件有用的事情是告诉您 种类 订购比较将提供什么。部分的,强烈的,或其他什么。这在理论上是有用的,但总体上是相当罕见的。
spaceship operator 由 Herb Sutter 提出并被委员会采纳用 C++ 20 实现,详细报告可以 consulted here, or you if are more into lectures, here you can see a video 本人制作它的情况。在报告的第 3/4 页,您可以看到主要用例:
C++20 之前版本所需的比较运算符实现如下 class:
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 ...
};
将被替换为:
class Point
{
int x;
int y;
public:
auto operator<=>(const Point &) const = default;
// ... non-comparison functions ...
};
所以为了回答你的问题,将 operator<=>
重载为 class 成员允许你使用 class 对象的所有比较运算符而无需实现它们,默认它也默认 operator==
,如果没有另外声明,它会自动实现 operator!=
,使所有比较操作都可用于单个表达式。此功能是主要用例。
我想指出,如果 C++20 不引入 the default comparison feature,spaceship 运算符将不可能实现。
Defaulted three-way comparison
[...]
Let R
be the return type, each pair of subobjects a
, b
is compared as follows:
[...]
... if R
is std::strong_ordering
, the result is:
a == b ? R::equal : a < b ? R::less : R::greater
Otherwise, if R
is std::weak_ordering
, the result is:
a == b ? R::equivalent : a < b ? R::less : R::greater
Otherwise (R
is std::partial_ordering
), the result is:
a == b ? R::equal : a < b ? R::less : b < a ? R::greater : R::unordered
Per the rules for any operator<=>
overload, a defaulted <=>
overload will also allow the type to be compared with <
, <=
, >
, and >=
.
If operator<=>
is defaulted and operator==
is not declared at all, then operator==
is implicitly defaulted.
它还允许仅默认 operator ==
,这将实现 operator !=
,尽管不如前者通用,但它也是一个有趣的可能性。
#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();
}
输出:
struct std::partial_ordering
struct std::strong_ordering
我知道如果操作数是整数类型,运算符returns一个PRvalue类型std::strong_ordering
。我也知道操作数是否具有浮点类型,运算符会产生类型为 std::partial_ordering
.
但是为什么我要使用三向比较运算符而不是双向比较运算符(==
, !=
, <
, <=
, >
, >=
)?这对我有什么好处吗?
主要优点(至少对我而言)是这个运算符可以默认为 class,这将自动支持您的 class 所有可能的比较。即
#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;
}
以前,所有这些操作都必须在 class 中定义,这很麻烦且容易出错 - 因为每当新成员添加到 [=15] 时,都必须记住重新访问这些操作=].
可以在一次操作中确定顺序。
其他运算符需要进行两次比较。
其他算子总结:
- 如果
a == b
为假,你不知道是a < b
还是a > b
- 如果
a != b
为真,你不知道是a < b
还是a > b
- 如果
a < b
为假,你不知道是a == b
还是a > b
- 如果
a > b
为假,你不知道是a == b
还是a < b
- 如果
a <= b
为真,你不知道是a == b
还是a < b
- 如果
a >= b
为真,你不知道是a == b
还是a > b
一个巧妙的副作用是所有其他运算符都可以根据 <=>
实现,并且编译器可以为您生成它们。
另一个副作用是,人们可能会对使用 <=>
作为数学中的等价箭头感到困惑,自打字机使用这三个符号以来,这几乎一直存在。
(当且仅当 a
和 b
是 不 等价时,我个人对 a <=> b
是“真实的”感到非常恼火。)
请自行判断。
飞船算子的重点是不是专门比较对象的。它的要点是允许编译器从 spaceship 运算符合成 other 比较运算符。
如果您不需要特别回答小于、等于或大于的问题,则无需直接调用它。使用对您有意义的运算符。
但是如果您需要使一个类型具有可比性,您只需编写 2 个函数(spaceship 和 equality)而不是 6 个。并且在编写这样的函数时,您可以在相关的各个类型上使用 spaceship 运算符(他们应该以这种方式进行比较)。这使得实现此类功能变得更加容易。
宇宙飞船操作员允许您做的另一件有用的事情是告诉您 种类 订购比较将提供什么。部分的,强烈的,或其他什么。这在理论上是有用的,但总体上是相当罕见的。
spaceship operator 由 Herb Sutter 提出并被委员会采纳用 C++ 20 实现,详细报告可以 consulted here, or you if are more into lectures, here you can see a video 本人制作它的情况。在报告的第 3/4 页,您可以看到主要用例:
C++20 之前版本所需的比较运算符实现如下 class:
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 ...
};
将被替换为:
class Point
{
int x;
int y;
public:
auto operator<=>(const Point &) const = default;
// ... non-comparison functions ...
};
所以为了回答你的问题,将 operator<=>
重载为 class 成员允许你使用 class 对象的所有比较运算符而无需实现它们,默认它也默认 operator==
,如果没有另外声明,它会自动实现 operator!=
,使所有比较操作都可用于单个表达式。此功能是主要用例。
我想指出,如果 C++20 不引入 the default comparison feature,spaceship 运算符将不可能实现。
Defaulted three-way comparison
[...]
LetR
be the return type, each pair of subobjectsa
,b
is compared as follows:
[...]
... ifR
isstd::strong_ordering
, the result is:a == b ? R::equal : a < b ? R::less : R::greater
Otherwise, if
R
isstd::weak_ordering
, the result is:a == b ? R::equivalent : a < b ? R::less : R::greater
Otherwise (
R
isstd::partial_ordering
), the result is:a == b ? R::equal : a < b ? R::less : b < a ? R::greater : R::unordered
Per the rules for any
operator<=>
overload, a defaulted<=>
overload will also allow the type to be compared with<
,<=
,>
, and>=
.If
operator<=>
is defaulted andoperator==
is not declared at all, thenoperator==
is implicitly defaulted.
它还允许仅默认 operator ==
,这将实现 operator !=
,尽管不如前者通用,但它也是一个有趣的可能性。