C++20尝鲜:<=>三路比较运算符(Three-way comparison)

C++20新特性:三路比较运算符<=>详解
C++20引入的三路比较运算符<=>,又称宇宙飞船运算符,简化了自定义类型比较。它支持与strcmp类似的用法,减少比较函数数量,如在C++17需18个,C++20仅需4个。运算结果可转换为布尔关系,并分为std::partial_ordering、std::weak_ordering和std::strong_ordering三种序关系类别,分别对应偏序、弱序和强序。在处理如浮点数、CaseInsensitiveString等类型时,该运算符尤其有用。
部署运行你感兴趣的模型镜像

三路比较运算符 <=> 也被称为宇宙飞船运算符(spaceship operator)。

三路比较运算符表达式的形式为:

左操作数<=>右操作数

为什么引入

如果项目中使用struct的值用作std::map的key,因此就需要自己实现比较运算符。如果实现得不对(可能会出现自相矛盾,a < b 且 b < a),程序可能会崩溃。

struct Name {
  string first_name;
  string mid_name;
  string last_name;
};
//C++11前
bool operator<(const Name& other) const {
  return first_name<other.first_name
      || first_name == other.first_name && mid_name < other.mid_name
      || first_name == other.first_name && mid_name == other.mid_name && last_name < other.last_name;
}
   //C++11后
bool operator<(const Name& other) const {
  return std::tie(first_name, mid_name, last_name) < 
      std::tie(other.first_name, other.mid_name, other.last_name);
}
//C++20
struct Name {
  string first_name;
  string mid_name;
  string last_name;
  //编译器默认实现,注入语义,强序
  std::strong_ordering operator<=>(const Name&) const = default;
};

在C++17, 需要 18 比较函数:

class CIString {
  string s;
public:
  friend bool operator==(const CIString& a, const CIString& b) {
    return a.s.size() == b.s.size() &&
      ci_compare(a.s.c_str(), b.s.c_str()) == 0;
  }
  friend bool operator< (const CIString& a, const CIString& b) {
    return ci_compare(a.s.c_str(), b.s.c_str()) <  0;
  }
  friend bool operator!=(const CIString& a, const CIString& b) {
    return !(a == b);
  }
  friend bool operator> (const CIString& a, const CIString& b) {
    return b < a;
  }
  friend bool operator>=(const CIString& a, const CIString& b) {
    return !(a < b);
  }
  friend bool operator<=(const CIString& a, const CIString& b) {
    return !(b < a);
  }

  friend bool operator==(const CIString& a, const char* b) {
    return ci_compare(a.s.c_str(), b) == 0;
  }
  friend bool operator< (const CIString& a, const char* b) {
    return ci_compare(a.s.c_str(), b) <  0;
  }
  friend bool operator!=(const CIString& a, const char* b) {
    return !(a == b);
  }
  friend bool operator> (const CIString& a, const char* b) {
    return b < a;
  }
  friend bool operator>=(const CIString& a, const char* b) {
    return !(a < b);
  }
  friend bool operator<=(const CIString& a, const char* b) {
    return !(b < a);
  }

  friend bool operator==(const char* a, const CIString& b) {
    return ci_compare(a, b.s.c_str()) == 0;
  }
  friend bool operator< (const char* a, const CIString& b) {
    return ci_compare(a, b.s.c_str()) <  0;
  }
  friend bool operator!=(const char* a, const CIString& b) {
    return !(a == b);
  }
  friend bool operator> (const char* a, const CIString& b) {
    return b < a;
  }
  friend bool operator>=(const char* a, const CIString& b) {
    return !(a < b);
  }
  friend bool operator<=(const char* a, const CIString& b) {
    return !(b < a);
  }
};

在C++20, 只需要4个:

class CIString {
  string s;
public:
  bool operator==(const CIString& b) const {
    return s.size() == b.s.size() &&
      ci_compare(s.c_str(), b.s.c_str()) == 0;
  }
  std::weak_ordering operator<=>(const CIString& b) const {
    return ci_compare(s.c_str(), b.s.c_str()) <=> 0;
  }

  bool operator==(char const* b) const {
    return ci_compare(s.c_str(), b) == 0;
  }
  std::weak_ordering operator<=>(const char* b) const {
    return ci_compare(s.c_str(), b) <=> 0;
  }
};

类似strcmp用法

三路比较运算结果支持用表达式 a <=> b == 0 或 a <=> b < 0 将转换三路比较的结果为布尔关系。

#include <compare>
#include <iostream>
 
int main(int argc, char *argv[]) {
    double val1 = -1.0;
    double val2 = 1.0;
 
    auto res = val1 <=> val2; //三路比较
 
    if (res < 0)
        std::cout << "val1 小于 val2";
    else if (res > 0)
        std::cout << "val1 大于 val2";
    else // (res == 0)
        std::cout << "val1 与 val2 相等";
    return 0;
}

缺省比较

  • 对于如下运算符:<、<=、>=、>, 如果<=>没有被定义,则这些运算符的默认实现即是将它们声明为deleted;否则,这些运算符将通过调用并判断<=>的返回值以确定两个操作数的大小关系。
  • 对于!=运算符:如果==没有被定义,则!=的默认实现即是将它声明为deleted;否则,!=的默认实现将调用==并将其返回值逻辑求反作为!=的返回值。

 

 
 
 

 

#include <compare>
#include <iostream>
 
template<typename T1, typename T2>
void TestComparisons(T1 a, T2 b)
{
    //测试<=>
    (a < b), (a <= b), (a > b), (a >= b);
    //测试 ==
    (a == b), (a != b);
}

struct S2
{
    int a;
    int b;
};

struct S1
{
    int x;
    int y;
    // 生成默认的
    auto operator<=>(const S1&) const = default;
    bool operator==(const S1&) const = default;
    // 实现S2
    std::strong_ordering operator<=>(const S2& other) const
    {
        if (auto cmp = x <=> other.a; cmp != 0)
            return cmp;
        return y <=> other.b;
    }

    bool operator==(const S2& other) const
    {
        return (*this <=> other) == 0;
    }
};

int main(int argc, char *argv[]) {
   TestComparisons(S1{}, S1{});
   TestComparisons(S1{}, S2{});
   TestComparisons(S2{}, S1{});
   return 0;
}

比较的类别(序关系)

  • std::partial_ordering,表示偏序关系

在偏序关系中,两个值可能会存在序关系,但并非任意两个值都存在序关系。

#include <iostream>
#include <cstdlib>
#include <cassert>
#include <limits>
#include <cmath>
 
int main()
{
    double a = 1.0;
    double b = 2.0;
    double c = std::numeric_limits<double>::quiet_NaN();
    //double比较是弱序
    std::partial_ordering ordering1 = a <=> b;;
    assert(ordering1 == std::partial_ordering::less);  // a < b

    std::partial_ordering ordering2 = b <=> a;
    assert(ordering2 == std::partial_ordering::greater);  // a > b

    std::partial_ordering ordering3 = a <=> c;
    assert(ordering3 == std::partial_ordering::unordered);  // NaN does not have partial ordering
}

浮点值之间的内建运算符 <=> 使用此顺序:正零与负零比较为 equivalent ,但能辨别,而 NaN 值与任何其他值比较为 unordered 。

  • std::weak_ordering,表示弱序关系

在弱序关系中,任意的两个值都存在序关系,可以比较大小。但序关系为相等的两个值不一定是不可辨别的。

// This file is a "Hello, world!" in C++ language by GCC for wandbox.
#include <iostream>
#include <cassert>
#include <string>
#include <algorithm>

struct Name {
    std::string name;
    //std::weak_ordering  operator<=>(const Name&) const = default;
};
std::weak_ordering operator<=>(const Name& lhs, const Name& rhs) noexcept {
    std::string s1 = lhs.name;
    std::transform(s1.begin(), s1.end(), s1.begin(),
                   [](unsigned char c){ return std::tolower(c); });
    std::string s2 = rhs.name;
    std::transform(s2.begin(), s2.end(), s2.begin(),
                   [](unsigned char c){ return std::tolower(c); });    

    return s1 <=> s2;
}

int main()
{
    Name a = {"WU"};
    Name b = {"wu"};
    std::weak_ordering ordering = a <=> b;
    assert(ordering == std::weak_ordering::equivalent);  // a == b
}

一个弱序类型的例子是 CaseInsensitiveString(大小写忽略字符串),它可以按原样存储原始字符串,但是以不区分大小写的方式比较。

  • std::strong_ordering,表示强序关系

在强序关系中,任意的两个值都存在序关系,可以比较大小。序关系为相等的两个值是不可辨别的。

#include <iostream>
#include <cstdlib>
#include <cassert>
#include <limits>
#include <cmath>

int main()
{
    int a = 1;
    int b = 2;
    int c = 2;
    //int 比较是强序
    std::strong_ordering ordering = a <=> b;
    assert(ordering == std::strong_ordering::less);  // a < b

    ordering = b <=> c;
    assert(ordering == std::strong_ordering::equivalent);  // b = c

最后,遇到问题多百度,多找大神问也是很重要的,建议进这个群一起聊,跟前辈一起探讨,也会得到很多帮助。也可以交流学习心得,技术问题,可以获取PDF书籍源码、教程等给大家免费使用 。

 

您可能感兴趣的与本文相关的镜像

Stable-Diffusion-3.5

Stable-Diffusion-3.5

图片生成
Stable-Diffusion

Stable Diffusion 3.5 (SD 3.5) 是由 Stability AI 推出的新一代文本到图像生成模型,相比 3.0 版本,它提升了图像质量、运行速度和硬件效率

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.luxsan</groupId> <artifactId>lingxi-ai-extend</artifactId> <version>1.0.0</version> </parent> <artifactId>lingxi-ai-Comparison</artifactId> <dependencies> <!-- SpringBoot Web容器 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>pdfbox</artifactId> <version>2.0.29</version> </dependency> <dependency> <groupId>org.json</groupId> <artifactId>json</artifactId> <version>20231013</version> </dependency> <!-- HanLP --> <dependency> <groupId>com.hankcs</groupId> <artifactId>hanlp</artifactId> <version>portable-1.8.4</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>com.luxsan</groupId> <artifactId>lingxi-ai-common-core</artifactId> </dependency> </dependencies> <build> <finalName>${project.artifactId}</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring-boot.version}</version> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project> 这个是我的依赖 然后我单独创建了一个服务 然后这个单独一个启动类 这个web服务嘛
07-04
<think>根据用户提供的pom.xml文件,我们可以分析项目依赖和配置,判断是否为Web服务。1.依赖分析:项目包含`spring-boot-starter-web`,这是SpringBoot用于构建Web应用的starter。2.打包插件:使用了`spring-boot-maven-plugin`,通常用于构建可执行的Spring Boot应用(包括Web应用)。3.启动类:用户提到有单独一个启动类,典型的Spring BootWeb应用会有一个带有`@SpringBootApplication`注解的启动类,并且会内嵌一个Web服务器(如Tomcat)。因此,可以确定这是一个Web服务项目。但是,用户之前提到过在测试时返回了400错误的HTML页面,现在又提供了pom.xml,我们需要确认依赖配置是否正确。注意:在之前的交流中,用户可能排除了Tomcat(但在这个pom.xml中没有显示排除Tomcat),所以这里没有排除Tomcat的配置,因此默认会使用Tomcat。然而,用户的问题在于测试返回了400错误的HTML页面,而不是JSON。这通常是因为没有设置正确的Accept请求头,或者没有配置SpringBoot的错误处理机制返回JSON。根据这个pom.xml,我们确认项目是Web服务,并且应该可以正常启动(因为包含了spring-boot-starter-web且没有排除Tomcat)。但是,如果用户想要返回JSON格式的错误信息,需要按照之前提供的解决方案进行配置(如配置全局异常处理或设置属性)。现在,用户的问题是:“这个web服务嘛”,所以直接回答:是的,这是一个Web服务。然后根据问题和回答提出相关问题。</think>### 项目类型分析及依赖确认 #### 项目类型确认 **是的,这是一个标准的Spring Boot Web服务项目**,主要依据如下: 1. **核心依赖** ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> ``` - 包含嵌入式Tomcat服务器(默认) - 提供Spring MVC框架支持 - 自动配置Web相关组件 2. **打包配置** ```xml <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> ``` - 使用Spring Boot的repackage目标 - 生成可执行的JAR文件(包含嵌入式服务器) 3. **启动类要求** - 需包含`@SpringBootApplication`注解的主类 - 标准启动方式:`SpringApplication.run(YourApplication.class, args)` #### 依赖完整性验证 | 依赖 | 状态 | 作用 | |------|------|------| | spring-boot-starter-web | ✅ 存在 | Web服务核心 | | spring-boot-maven-plugin | ✅ 存在 | 打包支持 | | pdfbox | ✅ 存在 | PDF处理 | | json | ✅ 存在 | JSON处理 | | hanlp | ✅ 存在 | NLP处理 | | lombok | ✅ 存在 | 简化代码 | | lingxi-ai-common-core | ✅ 存在 | 项目公共模块 | #### 服务启动验证步骤 1. 创建启动类: ```java package com.luxsan.lingxi; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ComparisonApplication { public static void main(String[] args) { SpringApplication.run(ComparisonApplication.class, args); } } ``` 2. 验证启动日志: ``` . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v${spring-boot.version}) Tomcat started on port(s): 8080 (http) ``` #### 常见问题预防 1. **端口冲突** 在`application.properties`中添加: ```properties server.port=8081 ``` 2. **上下文路径** ```properties server.servlet.context-path=/comparison ``` 3. **依赖冲突** 检查依赖树: ```bash mvn dependency:tree -Dincludes=org.springframework:*,tomcat:* ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值