前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++20新特性—“宇宙飞船”运算符

C++20新特性—“宇宙飞船”运算符

作者头像
CPP开发前沿
发布2021-12-04 15:08:26
1.6K0
发布2021-12-04 15:08:26
举报
文章被收录于专栏:CPP开发前沿CPP开发前沿

三路运算符是由Herb Sutter提出,2019年10月8日确定的C++20草案中正式将三路运算符纳入进来。在类中使用三路运算符后,编译器可以默认生成6个基础运算符,这一新特性的使用从一定程度上来说减少了开发的工作量,因此也受到大家的喜爱,被大家称为:宇宙飞船运算符。

1 三路运算符的形式

三路运算符不同于6中基础的运算符,C++标准委员会使用“<=>”作为三路运算符的操作符。表达式形式如下:

左操作数 <=> 右操作数

三路运算符返回的是一个对象,如下所示:

  • 如果左操作数 < 右操作数 则 (a <=> b) < 0
  • 如果左操作数 > 右操作数 则 (a <=> b) > 0
  • 如果左操作数 和 右操作数 相等/等价则 (a <=> b) == 0

三路运算符的返回结果和我们实际中使用的strcmp、strncmp一样,但是又有本质的区别,三路运算符返回的是std::strong_ordering、std::weak_ordering以及std::partial_ordering对象,而strcmp和strncmp返回的是整型数据。

2 默认比较

在编程时,类类型可以生成默认比较,在类中定义后,编译器会默认生成6种比较运算符的代码,生成默认比较的形式如下所示:

代码语言:javascript
复制
//类成员函数定义
返回类型 类名::operator运算符( const 类名 & ) const &(可选) = default;
//非成员函数
friend 返回类型 operator运算符( const 类名 &, const 类名 & ) = default;
//非成员函数,按值传递
friend 返回类型 operator运算符( 类名, 类名 ) = default;

三路运算符使用时,有自己限制,既:返回结果必须是auto或者std::strong_ordering、std::weak_ordering以及std::partial_ordering中的任意一个。

默认比较代码演示如下:

代码语言:javascript
复制
class Point {
  int x;
  int y;
public:
  Point(int _x,int _y):x(_x),y(_y){};
  //预制生成默认比较函数,编译器会生成<,==,<,<=,>=,!=运算代码
  auto operator<=>(const Point&) const = default;
};
int main() {
  Point pt1{1, 1}, pt2{1, 2};
  std::set<Point> s; // ok
  s.insert(pt1);     // ok
  std::cout << std::boolalpha
    << (pt1 == pt2) << ' ' // false;operator== 隐式地采用默认版本
    << (pt1 != pt2) << ' ' // true
    << (pt1 <  pt2) << ' ' // true
    << (pt1 <= pt2) << ' ' // true
    << (pt1 >  pt2) << ' ' // false
    << (pt1 >= pt2) << ' ';// false
}

代码运行结果为:

代码语言:javascript
复制
false true true true false false

3 用户自定义比较

实际编码时,有些场景需要我们自己定义三路运算符比较,编译器可以根据我们的定义规则生成相应的比较运算符代码。按照返回结果可以分为三种,分别是:

  • 强序:std::strong_ordering
  • 弱序:std::weak_ordering
  • 偏序:std::partial_ordering。

3.1 强序

返回对象为强序时,需要对每个对象进行比较操作,比价顺序可以根据需求自己定义。如下面的例子,使用强序作为返回对象。

代码语言:javascript
复制
struct TotallyOrdered {
  std::string tax_id;
  std::string first_name;
  std::string last_name;
public:
 // 自定义 operator<=> ,因为我们希望(在比较姓前)先比较名:
 std::strong_ordering operator<=>(const TotallyOrdered& that) const {
   if (auto cmp = last_name <=> that.last_name; cmp != 0)
       return cmp;
   if (auto cmp = first_name <=> that.first_name; cmp != 0)
       return cmp;
   return tax_id <=> that.tax_id;
 }
 // ... 非比较函数 ...
};

int main() {
  TotallyOrdered to1{"b","c","d"}, to2{"b","d","c"};
  auto res = to1<=>to2;
  if(res >0)
    std::cout<<"to1>to2"<<std::endl;
  else if(res < 0)
    std::cout<<"to1<to2"<<std::endl;
  else
    std::cout<<"to1==to2"<<std::endl;
}

运行结果为:

代码语言:javascript
复制
to1>to2

3.2 弱序

返回结果为std::weak_ordering,下面的代码就是生成对字符串比较时忽略大小写。代码如下:

代码语言:javascript
复制
class CaseInsensitiveString {
  std::string strS;
  int is_same(const CaseInsensitiveString& rhs) const {
        return strcasecmp(strS.c_str(),rhs.strS.c_str());
    }
public:
  CaseInsensitiveString(std::string _str):strS(_str){};
  std::weak_ordering operator<=>(const CaseInsensitiveString& b) const {
      if(is_same(b)>0) return std::weak_ordering::greater;
      else if(is_same(b)<0) return std::weak_ordering::less;
      else return std::weak_ordering::equivalent;
  }
};
int main() {
  CaseInsensitiveString str1("CPP"), str2("cpp");
  auto res = str1<=>str2;
  if(res >0)
    std::cout<<"to1>to2"<<std::endl;
  else if(res < 0)
    std::cout<<"to1<to2"<<std::endl;
  else
    std::cout<<"to1==to2"<<std::endl;
}

运行结果如下:

代码语言:javascript
复制
to1==to2

3.3 偏序

偏序是一种允许无法比较(无序)的值的比较的排序,比如包括 NaN 值的浮点排序。

代码语言:javascript
复制
class CaseInsensitiveString {
  std::string strS;
  int is_same(const CaseInsensitiveString& rhs) const {
        return strcasecmp(strS.c_str(),rhs.strS.c_str());
    }
public:
  CaseInsensitiveString(std::string _str):strS(_str){};
 std::partial_ordering operator<=>(const CaseInsensitiveString& b) const {
      if(is_same(b)>0) return std::partial_ordering::greater;
      else if(is_same(b)<0) return std::partial_ordering::less;
      else return std::partial_ordering::equivalent;
  }
};

int main() {
  CaseInsensitiveString str1("CPP"), str2("cpp");
  if(std::is_eq(str1<=>str2)) std::cout<<"str1 is str2";
  if(std::is_neq(str1<=>str2)) std::cout<<" str1 is not str2";
  if(str1>str2) std::cout<<" str1>str2";
  if(str1<str2) std::cout<<" str1<str2";
}

运行结果为:

代码语言:javascript
复制
str1 is str2

总结:

三路运算中,如果需要对类成员中每一个成员都进行比较,使用默认比较既可,如果不需要,则可以根据需要进行自定义生成。对于偏序小编的理解可能存在一定的差异,这里希望大家留言评论。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-12-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 CPP开发前沿 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档