我已经按照this particularly insightful answer提供的准则编写了一个模板类,该类实现一些基本的运算符重载:
template <typename _type>
class myClass {
// ...
template<_input_type> myClass<_type>& operator+=(const myClass<_input_type>& _other);
// ...
}
将算术复合运算符写为成员:
template <typename _type>
template <typename _input_type>
myClass<_type>& myClass<_type>::operator+=(const myClass<_input_type>& _other) {
static_assert(std::is_arithmetic<_type>::value);
// do stuff
return *this;
};
和非复合运算符作为非成员:
template <typename _type, typename _input_type>
inline myClass<_type> operator+(myClass<_type>& _o1, myClass<_input_type> _o2) {
return _o1+=_o2;
};
但是,由于模板myClass
可用于多种数据类型,其中一些非数字类型无法处理+
,-
,*
或/
运算符,因此我想知道将所有运算符重载代码实现为非成员函数的弊端是什么,例如我可以将它们全部放在单独的头文件中,仅在需要算术功能时才需要将其包括在内。我知道一种解决方案是定义一个新的class myNumericClass : public myClass
来实现操作符重载,但这将需要一个新的类型名并限制myClass
的多功能性。
答案 0 :(得分:4)
将复合赋值实现为非成员的主要缺点是与简单(复制或移动)赋值运算符不一致。一个简单的复制或移动分配(即operator=
)必须作为成员函数实现,否则编译器将完全拒绝该代码。
鉴于复制/移动赋值必须必须作为成员函数实现,许多人也希望将复合赋值也实现为成员。
顺便说一句,这段代码:
template <typename _type, typename _input_type>
inline myClass<_type> operator+(myClass<_type>& _o1, myClass<_input_type> _o2) {
return _o1+=_o2;
};
...,IMO,非常不可取。通用样式很好,但是您混淆了要按值传递哪个操作数和按引用传递哪个操作数。结果,它可能不必要地效率低下,和(更糟糕的是)修改了其左操作数,因此它的行为实际上类似于+=
而不是+
。您几乎可以肯定想要的是这样的:
template <typename _type, typename _input_type>
inline myClass<_type> operator+(myClass<_type> _o1, myClass<_input_type> const &_o2)
{
return _o1+=_o2;
};
在这里,我们按值传递左操作数,因此在调用该函数时,会从左操作数创建并初始化一个临时值。然后,我们修改该临时值(而不会更改)并返回它。由于我们确实将其返回,因此会有复制省略号(在较旧的编译器上是可选的,但自C ++ 17起是强制性的),这意味着它实际上通常只是对目标的引用,因此有效地类似于:{{1} }将被视为:a = b + c;
。由于我们只需要右操作数的前一个值,因此我们将其作为对const的引用来避免不必要的复制(尽管根据类型的不同,按引用传递可能不会获得足够的关注,甚至可能会造成损失。但是,这可能是一大收获,而损失却很少。)