繁体   English   中英

为什么要使用三向比较运算符 (<=>) 而不是双向比较运算符? 这有优势吗?

[英]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();
}

Output:

结构 std::partial_ordering
结构 std::strong_ordering

我知道如果操作数具有整数类型,则运算符返回std::strong_ordering 我还知道操作数是否具有浮点类型,运算符会产生std::partial_ordering类型的PRvalue

但是为什么我应该使用三向比较运算符而不是双向运算符( ==!=<<=>>= )? 这对我有好处吗?

它可以在一次操作中确定顺序
其他运算符需要两次比较。

其他运营商总结:

  • 如果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

一个简洁的副作用是所有其他运算符都可以根据<=>来实现,并且编译器可以为您生成它们。

另一个副作用是人们可能会对使用<=>作为数学中的等价箭头感到困惑,自从打字机获得这三个符号以来,几乎一直如此。
(当且仅当ab相等时,我个人对a <=> b如何“真实”感到非常恼火。)

主要优点(至少对我而言)是这个运算符可以默认为 class,这将自动支持 class 的所有可能比较。 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;
}

以前,所有这些操作都必须在 class 中定义,这既麻烦又容易出错 - 因为每当有新成员添加到 class 时,必须记住重新访问这些操作。

用你自己的判断。

飞船算子的重点不是专门用来比较物体的。 它的要点是允许编译器从 spaceship 运算符综合其他比较运算符。

如果您不需要特别回答小于、等于或大于的问题,则无需直接调用它。 使用对您有意义的运算符。

但是如果你需要使一个类型具有可比性,你只需要编写 2 个函数(宇宙飞船和相等)而不是 6 个。在编写这样的 function 时,你可以在有问题的各个类型上使用 spaceship 运算符(它们应该具有可比性吗?以这种方式)。 这使得实现这些功能变得更加容易。

宇宙飞船操作员允许您做的另一件有用的事情是告诉比较将提供什么的排序。 部分的,强烈的,或者其他的。 这在理论上可能很有用,但总体而言相当罕见。

飞船操作员由 Herb Sutter 提出并被委员会采用,并与 C++ 20 一起实施,详细报告可以在这里查阅,或者如果你更喜欢讲课,在这里你可以看到这个人自己为案例制作的视频它。 在报告的第 3/4 页中,您可以看到主要用例:

以下 class 中需要 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 ...
};

将被替换为:

class Point
{
    int x;
    int y;

public:
    auto operator<=>(const Point &) const = default; 
    // ... non-comparison functions ...
};

因此,要回答您的问题,将operator<=>重载为 class 成员允许您对 class 对象使用所有比较运算符,而无需实现它们,默认它也默认为operator== ,如果它没有另外声明,这在术语中自动实现operator!= ,使所有比较操作都可以通过单个表达式进行。 此功能是主要用例。

我想指出,如果不引入与 C++20 默认比较功能,那么宇宙飞船运算符是不可能的。

默认三向比较

[...]
R为返回类型,每对子对象ab比较如下:
[...]
...如果Rstd::strong_ordering ,结果是:

 a == b? R::equal: a < b? R::less: R::greater

否则,如果Rstd::weak_ordering ,则结果为:

 a == b? R::equivalent: a < b? R::less: R::greater

否则( Rstd::partial_ordering ),结果是:

 a == b? R::equal: a < b? R::less: b < a? R::greater: R::unordered

根据任何operator<=>重载的规则,默认的<=>重载也将允许将类型与<<=>>=进行比较。

如果operator<=>被默认并且operator==根本没有声明,那么operator==被隐式默认。

它还允许仅默认operator == ,这将实现operator != ,虽然不像前者那样通用,但它也是一个有趣的可能性。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM