C + + 20之前定义比较运算符

317 阅读2分钟

C + + 20简化了用户定义类型的比较定义,并引入了更好的处理方法。为此,引入了新的运算符 < = > (也称为宇宙飞船运算符)。

在 C + + 20之前定义比较运算符

在 C + + 20之前,您必须为一个类型定义六个运算符,以便为其对象的所有可能比较提供完全支持。

例如,如果希望比较 Value 类型的对象(具有整数 ID) ,则必须实现以下内容:

class Value { private: long id; public:

//equality operators:

bool operator== (const Value& rhs) const { return id ==rhs.id;

//busic check for equality }

bool operator!= (const Value& rhs) const { return !(*this == rhs);

//derived check }

//relational operators:

bool operator< (const Value& rhs) const { return id < rhs.id;

//basic check for ordering }

bool operator<=(const Value& rhs) const { return !(rhs <*this);

//derived check }

bool operator> (const Value& rhs) const { return rhs < *this;

//derived check }

bool operator>= (const Value& rhs) const { return !(*this <rhs);

//derived check }

};

这使您能够调用 Value (为该运算符定义的对象)与另一个 Value (作为参数 rhs 传递)的六个比较运算符中的任何一个。例如:

Value v1,v2; ...;

if(v1 <=v2){ //calls v1.operator<=(v2) }

运算符也可以间接调用(例如,通过调用 sort ()) :

std::vector<Value> coll;

...;

std::sort(coll.begin(),coll.end()); //uses operutor <to sort

由于 C + + 20,调用 mnight 可以选择使用范围:

std::ranges::sort(col1); //uses operator <to sort

问题是,尽管大多数运算符都是按照其他运算符定义的(它们都基于运算符 = = 或运算符 <) ,但是这些定义非常繁琐,而且添加了大量的视觉混乱。

此外,对于实现良好的类型,您可能需要更多:

· 如果运算符不能抛出

· 如果运算符可以在编译时使用,则使用 conexpr 声明它们

· 如果构造函数不是显式的,则将操作符声明为“隐藏的朋友”(在类结构中将其声明为朋友,以便两个操作数都成为参数并支持隐式类型转换)

· 用[[ nodiscard ]]声明运算符,以便在没有使用返回值时发出警告

例如:

lang/valueold.hpp class Value { private: long id;

public:

constexpr Value(long i) noexcept //supports implicit type conversion :id{i}{

} ...

//equality operators:

[[nodiscard]] friend constexpr

bool operator== (const Value& 1hs, const Value& rhs)

noexcept{ return 1hs.id==rhs.id;

// busic check for equulity }

[[nodiscard]] friend constexpr

bool operator!= (const Value& 1hs, const Value& rhs) noexcept { return!(1hs == rhs);

//derived check for inequality }

//relational operators:

[[nodiscard]] friend constexpr

bool operator< (const Value& 1hs, const Value& rhs) noexcept { return 1hs.id < rhs.id;

//busic check for ordering }

[[nodiscard]] friend constexpr

bool operator<= (const Value& 1hs, const Value& rhs) noexcept { return!(rhs <1hs);

//derived check }

[[nodiscard]] friend constexpr

bool operator> (const Value& 1hs,const Value& rhs) noexcept { return rhs<1hs;

//derived check }

[[nodiscard]] friend constexpr

bool operator>= (const Value& 1hs, const Value& rhs) noexcept { return!(1hs < rhs);

//derived check }

};

定义比较运算符

自从 C + + 20以来,有关比较运算符的一些事情发生了变化

要检查是否相等,现在只需定义 Operator = = 。

原因是,如果为了一个表达式!= b 找不到实现,编译器重写表达式并查找!(a = = b).如果这不起作用,编译器还会尝试交换操作数的顺序,以便它也尝试!(b = = a) :

a! = b//try: a! = b,! (a = = b) ,和! (b = = a)

因此,对于类型 A 和类型 B 的 b,编译器将能够编译

Q = ie 如果有的话

· 一个独立运算符! = (类型 A,类型 B)

· 一个独立运算符 = = (TypeA,TypeB)

· 一个独立运算符 = = (TypeB,TypeA)

· a 成员函数 TypeA: : 操作符! = (TypeB)

· a 成员函数 TypeA: : Operator = = (TypeB)

· a 成员函数 TypeB: : Operator = = (TypeA)

直接调用已定义的操作符!= 是首选的(但类型的顺序必须符合)。重新排序操作数的优先级最低。同时具有独立函数和成员函数是一个歧义错误。因此,与

bool operator==(const TypeA&,const TypeB&); or

class TypeA{ public:

bool operator==(const TypeB&) const; };

编译器将能够编译:

MyType a;
MyType b; ...
a==b;//OK:fits perfectly
b==a;//OK,rewritten as:a ==b 
a !=b; //OK, rewritten as:!(a==b) 
b !=a;//OK,rewritten as:!(a==b)

注意,由于进行了重写,当重写将第一个操作数转换为已定义成员函数的参数时,也可以进行第一个操作数的隐式类型转换。

了解如何通过仅定义成员运算符 = = 来获益于这个特性,这通常是一个迭代器!= 具有不同的顺序或操作数。

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