C++20中的三向比较运算符(three-way comparison operator)

news2025/1/11 19:51:27

      在C++20中,引入了一个新的特性,即"三向比较运算符(three-way comparison operator)",由于其外观,也被称为"宇宙飞船运算符(spaceship operator)",其符号为<=>。目的是简化比较对象的过程。这个运算符允许用户定义的对象之间进行全序比较,返回一个表示比较结果的枚举值,这个枚举值可以被用来决定两个对象是否相等、小于或大于另一个对象。使用<=>,需include头文件<compare>。

      当你为自定义类型定义<=>运算符时,编译器会自动为你生成==、<、>这三个比较运算符的默认实现。这大大减少了编写比较逻辑的工作量,并提高了代码的可读性和可维护性。

      <=>运算符确定两个对象A和B是A<B、A=B还是A>B:

      (1).<=>运算符是其它比较运算符的通用泛化(common generalization):>、==、<。使用<=>,在用户定义数据类型(如struct)的情况下,每个操作都可以以完全通用的方式实现;若不使用<=>,则必须逐一定义每个比较运算符。

      (2).对于字符串,它相当于C标准库中的std::strcmp函数。

      <=>运算符三种可能的结果类型支持所有六种关系运算符(==, !=, <, <=, >, >=)

      (1).类std::strong_ordering:有4种有效值:less、equal、equivalent、greater。

      (2).类std::weak_ordering:有3种有效值:less、equivalent、greater。

      (3).类std::partial_ordering:有4种有效值:less、equivalent、greater、unordered。

      注意:

      (1).试图将std::strong_ordering、std::weak_ordering、std::partial_ordering与整数字面值常量(integer literal)0以外的任何值进行比较的程序的行为是未定义的

      (2).std::strong_ordering可以隐式转换(implicitly-convertible)为std::weak_ordering;std::weak_ordering可以隐式转换为std::partial_ordering。

      以下为测试代码:

namespace {

template<typename T>
void compare(T x, T y)
{
    auto ret = x <=> y;
    std::cout << "name: " << typeid(ret).name() << std::endl;
    if (ret < 0)
        std::cout << "x < y:" << x << "," << y << std::endl;
    else if (ret == 0)
        std::cout << "x == y:" << x << "," << y << std::endl;
    else
        std::cout << "x > y:" << x << "," << y << std::endl;
}

struct Person {
    std::string name_{""};
    int age_{0};

    auto operator<=>(const Person& other) const noexcept
    {
        if (age_ != other.age_)
            return age_ <=> other.age_;
        
        if (name_ != other.name_)
            return name_ <=> other.name_;

        if (age_ == other.age_ && name_ == other.name_) {
            //return name_ <=> other.name_;
            return std::strong_ordering::equal;
        }
    }
};

} // namespace

int test_comparison_three_way()
{
    // numerical comparison
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<int> dist(0, 1000);

    auto x = dist(gen);
    auto y = dist(gen);
    compare(x, y);
    compare(y, x);
    compare(5, 5);

    // vector comparison
    const std::vector<int> a{ 1,2,3 }, b{ 1,2,4 };
    auto ret = a <=> b;
    if (ret < 0)
        std::cout << "a < b" << std::endl;
    else if (ret == 0)
        std::cout << "a == b" << std::endl;
    else
        std::cout << "a > b" << std::endl;

    // std::string comparison
    const std::string str1{ "Hello" }, str2{ "hello" };
    compare(str1, str2);
    compare(str2, str1);
    compare(std::string("hello"), std::string("hello"));

    // struct/class comparison
    const Person tom{ "Tom", 20 }, lisy{ "Lisy", 21 }, bob{ "Bob", 20 };

    ret = tom <=> bob;
    if (ret > 0) std::cout << "tom > bob" << std::endl;
    if (ret >= 0) std::cout << "tom >= bob" << std::endl;
    if (ret != 0) std::cout << "tom != bob" << std::endl;

    ret = tom <=> lisy;
    if (ret < 0) std::cout << "tom < lisy" << std::endl;
    if (ret <= 0) std::cout << "tom <= lisy" << std::endl;
    if (ret != 0) std::cout << "tom != lisy" << std::endl;
    if (ret == 0) std::cout << "tom == lisy" << std::endl;

    ret = tom <=> tom;
    if (ret == 0) std::cout << "tom == tom" << std::endl;

	return 0;
}

      执行结果如下图所示:

      C++20中的默认比较运算符函数(defaulted comparison operator function):可以显式请求编译器为类生成相应的默认比较

      类C的默认比较运算符函数是满足以下所有条件的非模板比较运算符函数(即<=>、==、!=、<、>、<=或>=):

      (1).它是某个类C的非静态成员或友元。

      (2).它在C中或在C完整的上下文中被定义为默认值。

      (3).它有两个const C&类型的参数或两个C类型的参数,其中隐式对象参数(如果有)被视为第一个参数。

      给定类C,默认比较顺序:按声明顺序排列

      (1).类C的直接基类子对象,按声明顺序排列。

      (2).类C的非静态数据成员,按声明顺序排列。如果任何成员子对象是数组类型,则按下标递增的顺序将其扩展为其元素的序列。扩展是递归的:数组类型的数组元素将再次扩展,直到没有数组类型的子对象为止。

      以下为测试代码:

namespace {

struct Person2 {
    // 注意: age_和name_定义的顺序不同,会产生不同的比较结果
    int age_{ 0 };
    std::string name_{ "" };
    //int age_{ 0 };

    auto operator<=>(const Person2&) const = default;
    //bool operator==(const Person2&) const = default;
};

} // namespace

int test_comparison_default()
{
    const Person2 tom{ 20, "Tom" }, lisy{ 21, "Lisy" }, bob{ 20, "Bob" };

    auto ret = tom <=> bob;
    if (ret > 0) std::cout << "tom > bob" << std::endl;
    if (ret >= 0) std::cout << "tom >= bob" << std::endl;
    if (ret != 0) std::cout << "tom != bob" << std::endl;

    ret = tom <=> lisy;
    if (ret < 0) std::cout << "tom < lisy" << std::endl;
    if (ret <= 0) std::cout << "tom <= lisy" << std::endl;
    if (ret != 0) std::cout << "tom != lisy" << std::endl;
    if (ret == 0) std::cout << "tom == lisy" << std::endl;

    ret = tom <=> tom;
    if (ret == 0) std::cout << "tom == tom" << std::endl;

    if (tom == tom) std::cout << "tom == tom" << std::endl;
    if (tom > bob) std::cout << "tom > bob" << std::endl;
    if (tom >= bob) std::cout << "tom >= bob" << std::endl;
    if (tom != lisy) std::cout << "tom != lisy" << std::endl;
    if (tom < lisy) std::cout << "tom < lisy" << std::endl;
    if (tom <= lisy) std::cout << "tom <= lisy" << std::endl;

    return 0;
}

      执行结果如下图所示:

      GitHub:https://github.com/fengbingchun/Messy_Test

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1896262.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaScript中闭包的理解

闭包&#xff08;Closure&#xff09;概念&#xff1a;一个函数对周围状态的引用捆绑在一起&#xff0c;内层函数中访问到其外层函数的作用域。简单来说;闭包内层函数引用外层函数的变量&#xff0c;如下图&#xff1a; 外层在使用一个函数包裹住闭包是对变量的保护&#xff0c…

用递归解决冒泡排序问题

冒泡排序是种很简单的排序方式. 如果用循环方式, 通常就是两层循环. 由于两层循环都是与元素个数 N 线性相关, 所以可以简单估算出它的时间复杂度是 O(N2), 通常而言, 这是较糟糕的复杂度. 当然, 这也几乎是所有简单方式的宿命, 想简单就别想效率高! 前面篇章说到递归也是一种循…

【LabView学习篇 - 1】:初始LabView

文章目录 初始LabView前面板和程序框图前面板&#xff08;Front Panel&#xff09;程序框图&#xff08;Block Diagram&#xff09;交互和工作流程 练手小案例&#xff1a;LabView中实现加法操作 初始LabView LabVIEW&#xff08;Laboratory Virtual Instrument Engineering W…

学习笔记——动态路由——OSPF(基础配置)

九、OSPF基础配置 1、OSPF基础配置 <Huawei>sys [Huawei]sys AR1 [AR1]un in en //取消配置回馈信息 [AR1]int g0/0/0 [AR1-GigabitEthernet0/0/0]ip add 10.1.12.1 24 //给ar1路由接口0配置IP地址 # 配置OSPF [AR1-GigabitEthernet0/0/0]ospf 1 router…

Java中的日期时间类详解(Date、DateFormat、Calendar)

1. Date类 1.1 概述 java.util.Date类表示特定的瞬间&#xff0c;精确到毫秒。Date类的构造函数可以把毫秒值转成日期对象。 继续查阅Date类的描述&#xff0c;发现Date拥有多个构造函数&#xff0c;只是部分已经过时&#xff0c;我们重点看以下两个构造函数 1.2 Date类构造…

三界-欢迎来到Web3D+GIS学习天地!

三界-欢迎来到Web3DGIS学习天地&#xff01; 地址&#xff1a;threelab.cn ** 坚持封装自己的引擎已经有三年了&#xff0c;每天都是加班熬夜开发功能&#xff0c;做东西。 虽然这段时间内&#xff0c;我一直在业余时间坚持开发&#xff0c;但实际投入的开发时间并不长&#…

iOS 练习项目 Landmarks (五):UISwitch 切换展示数据

iOS 练习项目 Landmarks &#xff08;五&#xff09;&#xff1a;UISwitch 切换展示数据 iOS 练习项目 Landmarks &#xff08;五&#xff09;&#xff1a;UISwitch 切换展示数据引入 Lookin优化项目结构纯代码方式重写主界面设置详情页的返回按钮的文本Switch切换TableView展示…

通江银耳销售管理系统-计算机毕业设计源码15998

摘要 随着人们健康意识的增强&#xff0c;银耳这种传统的中药食材备受关注。而通江银耳是四川省通江县特产&#xff0c;中国国家地理标志产品。四川省通江县是银耳的发源地&#xff0c;中国银耳之乡&#xff0c;通江银耳因主产于此而得名&#xff0c;以其独到的质厚、肉嫩、易炖…

【linux高级IO(一)】理解五种IO模型

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Linux从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学更多操作系统知识   &#x1f51d;&#x1f51d; Linux高级IO 1. 前言2. 重谈对…

Chrome插件分享-Stylus

简介 Stylus 是一个调整网页外观的用户样式管理器。它可以让您轻松为许多热门网站安装主题和皮肤。 这是 chrome 应用商店对Stylus插件的介绍&#xff0c;通俗一点讲&#xff0c;就是可以根据不同网站来定制网页的主题和皮肤&#xff0c;甚至可以去广告。 还有一个重点是&#…

高薪程序员必修课-java中 类加载器以及JVM类加载机制

前言 在Java中&#xff0c;类加载器&#xff08;ClassLoader&#xff09;是Java虚拟机&#xff08;JVM&#xff09;的一个重要组成部分&#xff0c;负责将.class文件加载到内存中并生成对应的Class对象。类加载器的主要任务是动态加载Java类&#xff0c;即在程序运行时根据需要…

刚办理的手机号被停用,你可能遇到这些问题了!

很多朋友都会遇到手机号被停用的情况&#xff0c;那么你知道你的手机号为什么会被停用吗&#xff1f;接下来&#xff0c;关于手机号被停用的问题&#xff0c;跟着小编一块来了解一下吧。 ​停机的两种形态&#xff1a; 1、第一个是局方停机&#xff0c;即语音、短信和流量都不…

Amazon SageMaker 机器学习之旅的助推器

一、前言 在当今的数字化时代&#xff0c;人工智能和机器学习已经成为推动社会进步的重要引擎。亚马逊云科技在 2023 re:Invent 全球大会上&#xff0c;宣布推出五项 Amazon SageMaker 新功能&#xff1a; Amazon SageMaker HyperPod 通过为大规模分布式训练提供专用的基础架构…

香橙派AIpro开发板评测:部署yolov5模型实现图像和视频中物体的识别

OrangePi AIpro 作为业界首款基于昇腾深度研发的AI开发板&#xff0c;自发布以来就引起了我的极大关注。其配备的8/20TOPS澎湃算力&#xff0c;堪称目前开发板市场中的顶尖性能&#xff0c;实在令人垂涎三尺。如此强大的板子&#xff0c;当然要亲自体验一番。今天非常荣幸地拿到…

Kubernetes基于helm安装 harbor

Kubernetes基于helm安装 harbor 之前harbor的安装都是借助docker完成一键安装部署&#xff0c;安装完成之后harbor组件均运行到一台机器上面&#xff0c;本文实践harbor在k8s环境中的部署。 准备工作 根据harbor官方要求&#xff1a; Kubernetes cluster 1.20Helm v3.2.0 …

kkFileView一款好用开源免费的文件在线预览项目

在这个数字化时代&#xff0c;我们每天都要跟各种文件打交道&#xff0c;但很多时候&#xff0c;文件预览却成了个头疼的问题&#xff0c;很多时候我们都希望能够在不下载文件的情况下&#xff0c;快速查看文件内容。 今天开源君就来分享一款文件在线预览项目 - kkFileView&am…

编译rust程序,并让它依赖低版本的GLIBC库

目录 方法一&#xff1a;在较低版本的linux系统里面编译更新centos源安装 gcc 方法二&#xff1a;静态编译 在linux环境下编译rust程序&#xff0c;编译好的程序会依赖你当前系统的GLIBC库&#xff0c;也就是说你的程序无法在使用更低版本GLIBC库的linux系统中运行。 查看当前系…

Java案例:完成用户登录

一案例要求&#xff1a; 二代码实现&#xff1a; Ⅰ package 重修;import java.util.Random; import java.util.Scanner;public class first {public static void main(String[] args) {javabean s1new javabean("张世杰","5201314");Scanner scnew Scan…

【Linux】Linux常用指令合集精讲,一篇让你彻底掌握(万字真言)

文章目录 一、文件与目录操作1.1 ls - 列出目录内容1.2 cd - 切换目录1.3 pwd - 显示当前目录1.4 mkdir - 创建目录1.5 rmdir - 删除空目录1.6 rm - 删除文件或目录1.7 cp - 复制文件或目录1.8 mv - 移动或重命名文件或目录1.9 touch - 创建空文件或更新文件时间戳 二、文件内容…