在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