C ++中重载算术运算符的最佳签名是什么?

时间:2020-03-06 14:34:33  来源:igfitidea点击:

我假设operator +的规范形式(假设存在重载的operator + =成员函数)是这样的:

const T operator+(const T& lhs, const T& rhs)
{
    return T(lhs) +=rhs;
}

但有人向我指出,这也行得通:

const T operator+ (T lhs, const T& rhs)
{
    return lhs+=rhs;
}

本质上,这种形式将临时的创建从实现主体转移到函数调用。

这两个参数具有不同的类型似乎有点尴尬,但是第二种形式有什么问题吗?是否有理由偏爱一个?

解决方案

对于已编辑的问题,首选第一种形式。编译器更有可能优化返回值(我们可以通过在T的构造函数中放置一个断点来验证此值)。第一种形式还将两个参数都当作const,这将是更可取的。

研究返回值优化主题,例如作为快速示例的此链接:http://www.cs.cmu.edu/~gilpin/c++/performance.html

我不确定两者的生成代码之间是否有太大差异。

在这两者之间,我(个人)更喜欢第一种形式,因为它更好地传达了意图。这涉及到++运算符的重用以及const&传递模板化类型的习惯。

我希望第一种形式具有可读性。

在看到第一个参数被复制之前,我不得不三思而后行。因此,由于这两个版本的效率都差不多,所以我会选择一个更易于阅读的版本。

const T operator+(const T& lhs, const T& rhs)
{
    return T(lhs)+=rhs;
}

如果我们想要简洁,为什么不这样做呢?

我的第一个想法是第二个版本可能比第一个版本无限快,因为没有引用作为参数推入堆栈。但是,这将非常依赖于编译器,并取决于例如编译器是否执行命名返回值优化。

无论如何,如果有任何疑问,请不要选择甚至可能根本不存在的很小的性能提升,而且我们很有可能不需要-选择最清晰的版本,这是第一个。

实际上,第二个是首选。如c ++标准中所述,

3.7.2/2: Automatic storage duration
  
  If a named automatic object has
  initialization or a destructor with
  side effects, it shall not be
  destroyed before the end of its block,
  nor shall it be eliminated as an
  optimization even if it appears to be
  unused, except that a class object or
  its copy may be eliminated as
  specified in 12.8.

即,因为使用复制构造函数创建了未命名的临时对象,所以编译器可能不会使用返回值优化。但是,对于第二种情况,允许使用未命名的返回值优化。请注意,如果编译器实现命名返回值优化,则最佳代码是

const T operator+(const T& lhs, const T& rhs)
{
    T temp(lhs);
    temp +=rhs;
    return temp;
}

我认为,如果将它们都内联(因为它们只是转发函数,并且可能是operator + =()函数是行外的,所以我会这样做),我们将几乎无法区分代码生成。也就是说,第一个更为规范。第二个版本是不必要的"可爱"。