【问题标题】:Arithmetic compound operator overload as non-member算术复合运算符重载为非成员
【发布时间】:2019-03-30 03:48:27
【问题描述】:

按照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 的多功能性。

【问题讨论】:

  • 将运算符放在另一个标题中没有明显问题。恕我直言,这是一个很好的解决方案。继承方案不好。

标签: c++ class templates operator-overloading


【解决方案1】:

作为非成员实现复合赋值的主要缺点是与简单(复制或移动)赋值运算符不一致。一个简单的复制或移动赋值(即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 起是强制性的),这意味着它通常实际上只是对目标的引用,非常有效,类似于:a = b + c; 将被视为:a = b; a += c;。由于我们只需要正确操作数的前一个值,我们将其作为对 const 的引用传递以避免不必要的复制(尽管根据类型,通过引用传递可能不足以关心,甚至可能是损失. 但它可以是一个很大的收获,而且很少超过一个微小的损失)。

【讨论】:

  • 我已经实现了复制和移动赋值运算符,只是将其排除在示例之外,因为它们似乎不相关。此外,代码中有一个错字,我实际上将两个参数作为非常量引用传递,但显然你的观点仍然存在,我会考虑到它。不过,将 both 操作数作为 const 引用传递不是更好吗,不会通过值传递它们中的任何一个创建不必要的中间副本吗?
  • @joaocandre 如果您无论如何都需要复制,那么按值传递就可以了
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-05
  • 2013-11-29
相关资源
最近更新 更多