3.3、编译器生成的比较操作符
看一下SpreadsheetCell的operator==与<=>的实现,它们只是简单地比较所有的数据成员。在这种情况下,可以将所需的代码进一步减少,因为C++20及以后的版本可以代你来写,例如,与拷贝构造函数一样可以显式缺省,operator==与<=>也可以被缺省,编译器来为你写并且以在类定义中声明的次序依次比较每个数据成员,这也叫做成员感知的扁序比较。
还有,如果你只是显式缺省了operator<=>,编译器也会自动包含一个缺省的operator==。所以,对于没有显式的double的operator==与<=>的SpreadsheetCell版本,你可以简单地写出下面的单行代码来为所有6个比较操作符添加支持,来比较两个SpreadsheetCell:
[[nodiscard]] std::partial_ordering operator<=>(const SpreadsheetCell&) const = default;
我们继续,可以使用auto作为operator<=>的返回值,这样的话编译器就会基于数据成员的<=>操作符的返回类型推断出返回类型:
[[nodiscard]] auto operator<=>(const SpreadsheetCell&) const = default;
如果不是所有的类数据成员都有可访问的operator==,那么类的缺省的operator==就会隐式地被删除掉。
如果类有不支持operator<=>的数据成员,缺省的operator<=>就会退一步使用那些数据成员的operator<与==。在这种情况下,返回类型就无法推断,需要显式指定返回类型为strong_ordering,partial_ordering,或weak_ordering。如果数据成员连可以访问的operator<与==都没有,那么缺省的operator<=>就会被隐式删除。
总结一下,为了能够让编译器写缺省的<=>操作符,类的所有的数据成员需要要么支持operator<=>,这种情况下返回类型可以是auto,要么支持operator<与==,这种情况下,返回类型就不能是auto。既然SpreadsheetCell有一个单独的double作为数据成员,编译器推断其返回类型为partial_ordering。
在本节的一开始,我提到单独的缺省operator<=>对于没有显式的double的operator==与<=> SpreadsheetCell版本是不灵光的。如果你确实加了显式的double版本,那你就是添加了一个用户声明的operator==(double)。因此,编译器就不会自动生成operator==(const SpreadsheetCell&),那你就得显式地缺省一个如下:
export class SpreadsheetCell
{
public:// Omitted for brevity[[nodiscard]] auto operator<=>(const SpreadsheetCell&) const = default;[[nodiscard]] bool operator==(const SpreadsheetCell&) const = default;[[nodiscard]] bool operator==(double rhs) const;[[nodiscard]] std::partial_ordering operator<=>(double rhs) const;// Omitted for brevity
};
如果为类显式缺省了operator<=>,推荐这样做而不是自己去实现。让编译器为你写,它会对于新加或者修改了的数据成员保持同步更新。如果你自己实现操作符,每当添加数据成员或者修改既有的数据成员时,需要记得去更新operator<=>的实现。如果operator==不是由编译器自动生成的话也是一样的。
只有当拥有参数为reference-to-const的类类型的操作符已定义,才可能会显式缺省operator==与<==>。例如,下面的代码是不灵的:
[[nodiscard]] auto operator<=>(double) const = default; // Does not work!
注意:为c++20及以后版本添加类的所有6个比较操作符的支持:
- 如果你的类的缺省的operator<=>好用,你所要做的就是一行代码显式缺省operator<=>作为成员函数。在某些情况下,可能也需要显式地缺省operator==。
- 否则的话,只要重载与实现operator==与<=>作为成员函数。
没有必要手工实现其它的比较操作符。