【问题标题】:Why should I use the three-way comparison operator (<=>) instead of the two-way comparison operators? Does this have an advantage?为什么要使用三向比较运算符 (<=>) 而不是双向比较运算符?这有优势吗?
【发布时间】:2021-07-20 21:01:03
【问题描述】:
#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();
}

输出:

结构 std::partial_ordering
struct std::strong_ordering

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

但是为什么我应该使用三路比较运算符而不是二路运算符(==!=&lt;&lt;=&gt;&gt;=)?这对我有好处吗?

【问题讨论】:

  • 这取决于我要说的上下文。如果您正在创建一个类,实现operator&lt;=&gt;(如果可能,甚至是defaulting)可以节省时间(开发和维护),而不必手动实现所有。另一方面,如果您有兴趣了解if( 1.1 &lt; 2.2 ),那么使用&lt;=&gt; 可能没有多大意义
  • 使用 2 个比较运算符 &lt;=&gt;== 编写的代码更少。与编写 6 个比较运算符相比,出现错误/错误的可能性更小。
  • 您是在询问使用&lt;=&gt; 进行比较,还是在任何一般情况下使用&lt;=&gt;
  • @DrewDormann 其实两者都有

标签: c++ c++20 comparison-operators spaceship-operator


【解决方案1】:

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

其他运营商总结:

  • 如果a == b为假,你不知道是a &lt; b还是a &gt; b
  • 如果a != b为真,你不知道是a &lt; b还是a &gt; b
  • 如果a &lt; b为假,你不知道是a == b还是a &gt; b
  • 如果a &gt; b为假,你不知道是a == b还是a &lt; b
  • 如果a &lt;= b为真,你不知道是a == b还是a &lt; b
  • 如果a &gt;= b为真,你不知道是a == b还是a &gt; b

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

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

【讨论】:

    【解决方案2】:

    主要优点(至少对我而言)是该运算符可以默认用于该类,这将自动支持您的类的所有可能比较。即

    #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;
    }
    

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

    【讨论】:

      【解决方案3】:

      使用您自己的判断。

      宇宙飞船运算符的重点不是专门用于比较对象的。它的要点是允许编译器从 spaceship 运算符合成 other 比较运算符。

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

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

      spaceship 运算符允许您做的另一件有用的事情是告诉比较排序将提供什么种类。部分的,强烈的,或者其他的。这在理论上很有用,但总体而言相当罕见。

      【讨论】:

      【解决方案4】:

      spaceship 操作符由 Herb Sutter 提出并被委员会采用,用 C++ 20 实现,详细报告可以consulted here,或者如果你更喜欢讲课,here you can see a video那个人自己为它辩护。在报告的第 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 ...
      };
      

      将被替换为:

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

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

      我想指出,如果没有在 C++20 中引入 the default comparison featurespaceship 运算符是不可能的。

      默认三路比较

      [...]
      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&lt;=&gt; 重载的规则,默认的&lt;=&gt; 重载还允许将类型与&lt;&lt;=&gt;&gt;= 进行比较。

      如果operator&lt;=&gt; 是默认的,而operator== 根本没有声明,那么operator== 是隐式默认的。

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

      【讨论】:

        猜你喜欢
        • 2021-11-07
        • 1970-01-01
        • 2021-06-14
        • 2018-06-11
        • 2018-05-08
        • 2018-09-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多