C++ 减少运算符重载,使用哪种方式
c++ less operator overload, which way to use?
例如:在C++头文件中,如果我定义了一个struct Record
并且我想使用它进行可能的排序,以便我想重载less operator
。以下是我在各种代码中注意到的三种方式。我粗略地注意到:如果我要把Record
放进一个std::set
,map
,priority_queue
,......容器,版本 2 有效(可能版本 3 也可以(;如果我要Record
保存到vector<Record> v
中,然后打电话给make_heap(v.begin(), v.end())
等。那么只有版本 1 有效。
struct Record
{
char c;
int num;
//version 1
bool operator <(const Record& rhs)
{
return this->num>rhs.num;
}
//version 2
friend bool operator <(const Record& lhs, const Record& rhs) //friend claim has to be here
{
return lhs->num>rhs->num;
}
};
例如,在同一个头文件中:
//version 3
inline bool operator <(const Record& lhs, const Record& rhs)
{
return lhs->num>rhs->num;
}
基本上,我想在这里抛出问题,看看是否有人可以提出一些总结,这三种方法之间有什么区别,每个版本的正确位置是什么?
它们本质上是相同的,除了第一个是非常量并允许您修改自身。
我更喜欢第二个,原因有两个:
- 它不一定是
friend
. -
lhs
不一定是Record
定义 less 运算符的最佳方法是:
struct Record{
(...)
const bool operator < ( const Record &r ) const{
return ( num < r.num );
}
};
欢迎来到 c++20,我们有更多的选择。
//version 1
bool operator <(const Record& rhs)
{
return this->num>rhs.num;
}
这个是错误的,它应该读作:
//version 1
bool operator <(const Record& rhs)const
{
return this->num>rhs.num;
}
因为您希望左侧也符合 CONST 资格。
//version 2
friend bool operator <(const Record& lhs, const Record& rhs) //friend claim has to be here
{
return lhs->num>rhs->num;
}
这个是对称的。 所以假设你有一个struct Bar
operator Record
.
然后
Record rhs;
Bar lhs;
assert( lhs < bar );
上述适用于对称情况,但不适用于成员函数版本。
类版本中的friend
是一个运算符,只能通过 Koenig 查找(参数相关查找(找到。 当您希望将对称运算符(或类型位于右侧的运算符,如 ostream&<<*this
(绑定到特定模板类实例时,它非常有用。
如果它在类之外,它必须是模板函数,并且模板函数的重载方式与非模板函数不同;非模板函数允许转换。
template<class T>
struct point {
T x ,y;
point operator-(point const& rhs)const{
return {x-rhs.x,y-rhs.y};
}
friend point operator+(point const& lhs, point const& rhs) {
return {lhs.x+rhs.x, lhs.y+rhs.y};
}
};
template<class T>
point<T> operator*( point<T> const& lhs, point<T> const& rhs ) {
return {lhs.x*rhs.x, lhs.y*rhs.y};
}
这里的-
是不对称的,所以如果我们有一个类型转换为左侧的point<int>
,-
将找不到。
+
是对称的,是"Koenig 运算符",因此它是非模板运算符。
*
是对称的,但是一个模板运算符。 如果你有转换为点的东西,它不会找到*
重载,因为扣除会失败。
//version 3
inline bool operator <(const Record& lhs, const Record& rhs)
{
return lhs->num>rhs->num;
}
这类似于上面的template
,但这里不会出现该问题。 这里的区别在于,你可以在类外获取这个函数的地址,而你写的"koenig operator<
"只能通过ADL找到。 哦,这不是朋友。
C++17 添加
auto operator<=>(const Record&)=default;
我们使用宇宙飞船操作员<=>
自动定义排序。
这将使用 c
和 num
的排序来生成所需的结果。
就像 5 法则一样,您应该设法使这里的=default
正常工作。 声明<
忽略是一种难闻的气味,纠缠您状态的不同部分也是如此。
- 成员
- 函数的非成员等效项
bool operator <(const Record& rhs);
是
bool operator <(Record& lhs, const Record& rhs); // lhs is non-const
现在,STL 容器将它们存储的项目视为const
(至少只要比较运算符是这样(。所以他们称const-const
运算符的变体。如果他们找不到它(如果您仅提供变体 1( - 这是一个编译错误。
- 如果您同时提供
const-const
会员和const-const
非会员:
struct Record
{
bool operator <(const Record& rhs) const;
};
bool operator <(Record& lhs, const Record& rhs);
这是另一个编译器错误,因为这样的定义会导致歧义:
如果在找到匹配项的最高级别找到两个匹配项,呼叫因不明确而被拒绝。/斯特劳斯特鲁普,C++,第 12.3.1 节/
- 最后,(如前面的答案所述(不需要
friend
修饰符,因为默认情况下struct
的所有字段都是公共的。
PS make_heap
不希望比较的项目const
,因为它是一个更底层的野兽,使用它就像共同创作一个新的基于堆的库,所以你有责任跟踪项目的恒常性。
PPS set
将项目视为const
并不能防止您在将项目插入容器后修改项目的键 - 如果您尝试这样做,将导致运行时错误(段错误(。
在类中支持,除非它不能在类中,因为第一个参数是错误的类型。
- C++ ->运算符 [] 的快捷方式
- C++:使用 std::unique_ptr 访问重载运算符++的最佳方式?
- 逗号运算符在对齐方式中
- 是否有快捷方式重叠运算符?
- 重载运算符是具有较小默认对齐方式的新增运算符
- 为什么三元运算符在编译时的工作方式与运行时不同?
- 对运算符删除覆盖的工作方式感到困惑
- 重载小于 (<) 运算符,用于以多种方式对对象进行排序
- 重载输入/输出运算符,为什么它以这种方式工作而不是以另一种方式工作
- Clang和g++处理运算符过载的方式不同
- 从BoostCon谈话中重载取消引用运算符的奇怪方式
- C++ 自定义运算符(+=) 的行为方式不可预测
- 运算符>>(,) 重载以令人惊讶的方式运行
- 比率类中的运算符/=以错误的方式工作
- 运算符 [] 和 insert() 函数在 C++ 映射中的行为方式不应该相同吗?
- 提供相同的ostream和wostream流运算符的任何快捷方式
- 复制构造函数未被调用,无法理解赋值运算符的工作方式
- C++ 字符串比较运算符>根据两个字符串的比较方式生成不同的输出
- 三元运算符是否以定义的方式短路
- 在派生类和切片中使用运算符=的正确方式