C++20 默认比较运算符

274 阅读4分钟

上一章节我们说到三路比较运算符,本章作为其姊妹篇继续补充其相关知识。

1. 默认<=>

默认运算符<=>通过依次比较自定义数据类型的父类(从左到右深度优先)和自己的非静态成员对象来执行字典顺序比较,以计算<=>,同时会递归地比较数组成员(按下标递增的顺序),并在发现不相等的结果时提前停止。同时,是否对于虚基类的子对象的比较是不确定的。

1.1 返回值类别

如果重载操作符<=>函数的返回值为auto,实际返回类型是要比较的基类和自己的成员对象和成员数组元素的通用比较类别。参见

template <class... _Types>
using common_comparison_category_t =
    conditional_t<(_Classify_category<_Types...> & _Comparison_category_none) != 0, void,
        conditional_t<(_Classify_category<_Types...> & _Comparison_category_partial) != 0, partial_ordering,
            conditional_t<(_Classify_category<_Types...> & _Comparison_category_weak) != 0, weak_ordering,
                strong_ordering>>>;

有上式可知,其返回类型存在四种情况:void、partial_ordering、weak_ordering、strong_ordering。

  • void 类型通常表示两个对象之间的比较无法进行或者不可确定,例如当两个对象的类型之间没有定义 <=> 操作符时,但是编译器会针对不可比较的类型(即返回为void的类型)报编译错误,故通常情况下不会返回void型。
  • strong_ordering: 表示严格的强顺序。如果两个值不相等,返回正数或负数,以表示它们的相对顺序。如果相等,返回零。
  • weak_ordering: 表示弱顺序。除了等于时返回零外,与 strong_ordering 类似。相等时返回零,不相等时返回正数或负数。
  • partial_ordering: 表示部分顺序。在可能无法确定比较结果的情况下使用。如果无法比较,则返回 partial_ordering::unordered。如果两个值相等,返回 partial_ordering::equivalent。 由以上可知,三路比较运算符的返回结果并不是数值,而是预定义的枚举
enum class _Compare_eq : _Compare_t { equal = 0, equivalent = equal };
enum class _Compare_ord : _Compare_t { less = -1, greater = 1 };
enum class _Compare_ncmp : _Compare_t { unordered = -128 };

三种类型返回值和枚举的对应关系如下表所示:

a<=>b偏序类型
a<bpartial_ordering::less_Compare_ord::less
a==bpartial_ordering::equivalent_Compare_eq::equivalent
a>bpartial_ordering::greater_Compare_ord::greater
非可比较的值,如NaNpartial_ordering::unordered_Compare_ncmp::unordered
------
a<bweak_ordering::less_Compare_ord::less
a==bweak_ordering::equivalent_Compare_eq::equivalent
a>bweak_ordering::greater_Compare_ord::greater
------
a<bstrong_ordering::less_Compare_ord::less
a==bstrong_ordering::equal_Compare_eq::equal
a>bstrong_ordering::greater_Compare_ord::greater

根据任何运算符<=>重载的规则,一个<=>的默认重载还将允许将类型与<、<=、 >=、> 进行比较。如果 operator<=> 是默认值,并且根本没有声明 operator==,则 operator== 是隐式默认值。

2.自定义<=>

当默认语义的<=>不满足需求时,程序员可以自定义<=>。其返回值类型可以有三种,强序、弱序、部分顺序,如何确定应该使用哪种返回值呢?

  • 强序:要求比较自定义数据类型内的所有成员(含基类),但顺序与默认值不同。切记一定要比较所有成员,出现遗漏则替换行可能会受到影响。
  • 弱序:不能满足比较所有成员的条件时或不能满足完全相等时(大小写),返回弱序。
  • 部分顺序:允许不可比较值的排序(如Nan),可以是数据类型中成员变量的一部分不参与比较

3. 默认==比较

返回bool型的默认==运算符重载将会基于声明顺序对基类和成员变量依次进行==比较,所有值均相等则相等,遇到非相等值立即返回。 重载==的数据类型也支持进行!=。

struct Point
{
	int x;
	int y;
	bool operator==(const Point&p) const =default;
	// ... non-comparison functions ...
};
// compiler generates element-wise equality testing

void using_default_equal()
{
	Point pt1{ 3, 5 }, pt2{ 2, 5 };
	std::cout << std::boolalpha
		<< (pt1 != pt2) << '\n'  // true
		<< (pt1 == pt1) << '\n'; // true
}

4.其他默认比较操作运算符

四个关系运算符(<,>,<=,>=)均可以显式指定为预置。预置的关系运算符必须返回 bool。 如果 x <=> y 的重载决议失败,或operator@(<,>,<=,>=)无法被应用到 x <=> y 的结果,则这种操作符(<,>,<=,>=)被废弃(deleted)

【欢迎关注关注号:程序员的园】