c ++ 使用比较运算符 < = >

403 阅读4分钟

使用比较运算符 < = >

运算符 < = > 应该返回一个比较类别类型之一的值,表示比较结果以及该结果是否能够创建强/总排序、弱排序或部分排序的信息。

例如,这就是为 MyType 类型定义独立运算符 < = > 的方法:

 std::strong_ordering operator<=> (MyType x, MyOtherType y)

{

if (xlsEqualToY)

return std::strong_ordering::equal; if (xlsLessThanY)

return1 std::strong_ordering::less; return std::strong_ordering::greater;

}

或者作为一个更具体的例子,为 MyType 类型定义运算符 < = > :

class MyType {

..-

std::strong_ordering operator<=> (const MyType& rhs) const { return value == rhs.value ? std::strong_ordering::equal :

value <rhs.value ? std::strong_ordering::less :

std::strong_ordering::greater; }

};

但是,通过将运算符映射到基础类型的结果来定义运算符通常更容易。因此,上面的成员运算符 < = > 最好只生成其成员值的值和类别:

class MyType { ..-

auto operator<=> (const MyType& rhs) const { return value <=> rhs.value;

} };

这不仅返回正确的值,还确保返回值具有正确的比较类别类型,具体取决于成员值的类型。

调用操作符 < = >

你可以直接调用任何定义操作符< = >:

 MyType x,y;

...

x<=>y

如前所述,运算符 < = > 是为所有基本类型预定义的,关系运算符是为这些基本类型定义的:

int x=17,y=42; x<=>y

//yields std::strong_ordering::less


x<=>17.0 //yields std::partial_ordering::equivalent 

&x <=> &x //yields std::strong_ordering::equal

 &x <=> nullptr //ERROR:relational comparison with nullptr not supported

此外,所有提供关系运算符的 C++标准程式库现在也提供运算符 < = > ,例如:

std::string{"hi"} <=> "hi"   //yields std::strong_ordering::equal; 

std::pair{42, 0.0} <=> std::pair{42,7.7}   //yields std::partial_ordering::less

对于您自己的类型,您只需将运算符 < = > 定义为成员或独立函数。

请记住,返回类型取决于比较类别。您可以检查特定的返回值:

if (x <=> y == std::strong_ordering::equal) //might not compile

然而,与0进行比较总是可能的,而且通常更容易:

if (x <=> y ==0)

此外,由于关系运算子调用的新改写, < = > 可能会被间接调用:

 if (!(x < y ll y < x)) //might call operator<=> to check for equality

或者:

if(x<=y&& y<=x)  //might call operator<=>to check for equality

注意那个operator!= 永远不会被重写为调用操作符 < = > 。但是,它可能会调用由默认运算符 < = > 成员隐式生成的 operator<=>member 。

处理多个运算符

为了基于多个属性计算运算符 < = > 的结果,通常只需要实现一个子比较链,直到结果不相等/等价,或者到达要比较的最终属性

class Person {

.--

auto operator<=>   (const Person& rhs) const

 { auto cmp1 = lastname <=> rhs.lastname;  //primary member for ordering

 if (cmp1 != 0) return cmp1  //return result 

if not equal auto cmp2 = firstname <=> rhs.firstname;  //secondary member for ordering

 if (cmp2 != 0) return cmp2;  //return result ifnot equal 

return value <=> rhs.value;  //final member for ordering }

}; 

但是,如果属性具有不同的比较类别,则不会编译返回类型。例如,如果一个成员名称是字符串,而一个成员值是 double,那么返回类型就会发生冲突:

class Person {

std::string name; double value;

··.

auto operator<=> (const Person& rhs) const { //ERROR: different return types deduiced 



auto cmp1 = name <=> rhs.name;

if (cmp1 != 0) return cmp1;  //return strong_ordering.for std::string 



return value <=> rhs.value ; //return partial_ordering for double }

};

在这种情况下,您可以定义转换到最弱的比较类型。如果你知道最弱的比较类型,你可以直接用它作为返回类型:

class Person { 

std::string name;

double value; 

...

std::partial_ordering operator<=> (const Person& rhs) const { //OK 



auto cmp1 =name <=> rhs.name;

if (cmp1 !=0) return cmp1; //strong_ordering converted to return type 

return value <=> rhs.value; //partial_ordering used us the return type }

};

如果您不知道比较类型(例如,它们的类型是一个模板参数) ,您可以使用一个新的类型 trait std: : common _ ratio _ type < > 来计算最强的比较类别:

class Person { std::string name;

 double value; 

...

 auto operator<=> (const Person& rhs) const

→>std::common_comparison_category_t<decltype(name <=> rhs.name),

                                                      decltype(value <=> rhs.value)> { 



auto cmp1= name <=> rhs.name;

if (cmp1 !=0) return cmp1; //used as or converted to common comparison type



 return value <=> rhs.value; // used as or converted to common comparison type }

};

通过使用尾随返回类型语法(前面是 auto,后面是 return type->) ,我们可以使用参数来计算比较类型。虽然在这种情况下可以只使用 name 而不是 rhs.name,但是这种方法通常是有效的(例如,也适用于独立函数)。

如果希望提供比内部使用的更强的类别,则必须将内部比较的所有可能值映射到返回类型的值。如果无法映射某些值,则可能包括一些错误处理。例如:

class Person{

 std::string name; double value;

 ...

std::strong_ordering operator<=> (const Person& rhs) const {

 auto cmp1=x.name <=>y.name;

if (cmp1 !=0) return cmp1; //return strong_ordering for std::string 

auto cmp2 = x.value <=>y.value;//might be partial_ordering for double

 //map partial_ordering to strong_ordering:

assert(cmp2 != std::partial_ordering::unordered); //RUNTIME ERROR ifunordered

 return cmp2 == 0 ? std::strong_ordering::equal

                           :cmp2 > 0? std::strong_ordering::greater

                           :std::strong_ordering::less; }

};

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 N 天,点击查看活动详情