C++20新特性—三路比较符<=>

集合(set,map等)算法通常会涉及元素比大小,因此两个元素(类型)的比较操作就显得额外重要。两个类型之间的共有六种比较符(<,<=,>,>=,==,!=),如果需要完整的表达,就要写六个函数,如果需要与可转换类型比较,这项工作显得比较繁琐,因此这也是C++引入<=>的基本初衷。【如果认为用一个比较符,例如”<”就可以推导出另外几个,其实这只适用于一些场景,这也是C++引入新的比较操作符的另一个原因。】

<=>称为3-way comparison operator(三路比较符),它的定义如下:

a <=> b 返回一个类型,该类型在a < b,,a > b,a等效b三种情况下与0的比较分别是小于0,大于0,等于0。

上面的0仅是一个字面量,最好不要理解成int 0。按照其定义,原来两个类型之间的六种比较符(<,<=,>,>=,==,!=),都可采用<=>来表达,即:

a@b可表达为a<=>b @ 0或 0 @ b<=>a

其中@就是上面六种比较符,例如a<b可表达为a<=>b <0或 0 < b<=>a(编译器会尝试前者,不成功再尝试后者)。

上面定义中,<=>的返回值是一个对象(模板类),它的类型可以是:

  • std::strong_ordering
  • std::weak_ordering
  • std::partial_ordering

为理解它们的意义,先说明一下如果对一个集合中的元素进行排序,可能出现的情况:

  • 完全且严格顺序:所有元素都可比较,且有严格的顺序关系(可比较,等值唯一)
  • 完全且松散顺序:所有元素都可比较,但存在近似相等的情况(可比较,等值不唯一)
  • 不完全顺序:元素存在不可比较的情况(非全可比较,等值也不唯一)

如整数集合属于第一种情况,浮点数集合、大小写不区分的字符集合属于第二种情况,包含空值的字符集、包含NaN的数值属于第三种情况,很明显,这三种情况就分别对应std::strong_ordering,std::weak_ordering和std::partial_ordering。

标准库已定义了若干上述类型的静态对象,例如std::partial_ordering类型定义了4个:

  • std::partial_ordering::less
  • std::partial_orderingequivalent
  • std::partial_ordering::greater
  • std::partial_ordering::unordered

而std::strong_ordering定义了4个,std::weak_ordering定义了3个。这些静态对象就是操作符<=>的返回值,它们与字面量0的操作示例如下,结果与以前对<=>的定义是一致的,需要特别注意的是std::partial_ordering::unordered与0的比较都是false。

strong_ordering::less < 0     // true
strong_ordering::less == 0    // false
strong_ordering::less != 0    // true
strong_ordering::greater >= 0 // true

// unordered is a special value that isn't comparable against anything
partial_ordering::unordered < 0  // false
partial_ordering::unordered == 0 // false
partial_ordering::unordered > 0  // false

可以想象,strong_ordering类型是可以转换为相应的weak_ordering或partial_ordering类型,但反过来却不行。

在重载(自定义)类型的<=>时,会用到这些静态对象。

<=>应用的一个基本例子如下,其中三种类型的比较都应用了系统缺省的<=>实现(尽量利用系统缺省实现,这也是推荐做法),输出都是1(true)。

void test_compare() 
{
    struct ts {
      int i;
      char c;
      float f;
      auto operator<=>(const ts&) const = default;
    };
    
    int ia=1, ib=2;
    auto cmp1=ia<=>ib;
    std::cout<<(cmp1<0)<<std::endl;    
    
    std::vector<int> va={1,2,3};
    std::vector<int> vb={2,3};    
    auto cmp2=va<=>vb;
    std::cout<<(cmp2<0)<<std::endl;        
    
    ts ta = { 0, 'c', 1.0f};  
     ts tb={ 0, 'c', 1.0f};
   std::cout<< (ta==tb) <<std::endl;

}

下面是一个我们重载<=>的一个示例。

void test_compare() 
{
    struct ts {
      int id;
      float f;
      std::partial_ordering operator<=>(const ts& that) const
      {
          if(id<0 || that.id<0)  return std::partial_ordering::unordered;
          if(f>that.f) return std::partial_ordering::greater;
          if(f<that.f) return std::partial_ordering::less;          
          return std::partial_ordering::equivalent;
      }
      bool operator==(const ts& that) const
      {
          return (*this<=>that)==0;
      }            
    };
    
    ts ta = {2, 1.0f};  
    ts  tb={3, 1.0f};
    std::cout<< (ta==tb) <<std::endl;
    std::cout<< (ta>tb) <<std::endl;
}

上面示例中,只有在id大于0时才进行比较。当然这个示例没有什么实际意义。要注意的是,目前重载<=>后,>=,>,<=,<可自动调用<=>并做相应转换,==!=还需单独编写==,如上例所示,如果去掉==重载,则ta==tb不能编译通过。因此,有下表的关系(来自于https://brevzin.github.io/c++/2019/07/28/comparisons-cpp20):

 

Equality

Ordering

Primary

==

<=>

Secondary

!=

<, >, <=, >=

其中,Secondary行中的比较符可由对应primary行中的比较符隐式转换而来,也就是一个新类型的六种比较关系现在由两种比较来完成。

 

 

 

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
std::shared_ptr<T>是C++11中引入的智能指针之一。它是一个模板类,用于管理动态分配的对象。多个std::shared_ptr可以同时拥有同一个对象的所有权,它们会共享该对象的引用计数。当最后一个std::shared_ptr被销毁时,它会自动释放所管理的对象。 以下是std::shared_ptr<T>的一些常见用法: 1. 创建一个新的std::shared_ptr并指向动态分配的对象: std::shared_ptr<T> ptr(new T); 2. 使用std::make_shared<T>函数创建一个新的std::shared_ptr并指向动态分配的对象: std::shared_ptr<T> ptr = std::make_shared<T>(); 3. 使用std::shared_ptr的拷贝构造函数创建一个新的std::shared_ptr并共享同一个对象: std::shared_ptr<T> ptr1(new T); std::shared_ptr<T> ptr2 = ptr1; 4. 使用std::shared_ptr的赋值操作将一个std::shared_ptr指向另一个std::shared_ptr所管理的对象: std::shared_ptr<T> ptr1(new T); std::shared_ptr<T> ptr2(new T); ptr1 = ptr2; 需要注意的是,std::shared_ptr使用引用计数来跟踪对象的所有权,因此当最后一个std::shared_ptr被销毁时,它会自动释放所管理的对象。引用计数的加一和减一操作是原子性的,这意味着在多线程环境下,std::shared_ptr的使用是线程安全的。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [c++11新特性之智能指针std::shared_ptr、std::weak_ptr、std::unique_ptr的使用](https://blog.csdn.net/Jason_Chen__/article/details/124302757)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [C++11 std::shared_ptr总结与使用示例代码详解](https://download.csdn.net/download/weixin_38677725/14841436)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值