C ++是否需要重载运算符的特殊rvalue变量?

时间:2017-02-04 10:09:36

标签: c++ operator-overloading rvalue-reference rvalue

在诸如Operator Overloading之类的问题+答案中,指出重载二元运算符(例如operator+)的最佳方法是:

class X {
  X& operator+=(const X& rhs)
  {
    // actual addition of rhs to *this
    return *this;
  }
};
inline X operator+(X lhs, const X& rhs)
{
  lhs += rhs;
  return lhs;
}

operator+本身按lhs按值rhs按const引用,并按值返回已更改的lhs

如果将rvalue作为lhs调用,我无法理解这里会发生什么:这仍然是需要的单一定义(并且编译器会优化参数的移动和返回值),或者添加第二个重载版本的运算符与rvalue引用一起使用是否有意义?

修改

有趣的是,在Boost.Operators中,他们讨论了这种实现:

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

允许命名返回值优化,但默认情况下不使用它,因为:

  

可悲的是,并非所有编译器都实现了NRVO,有些甚至以不正确的方式实现它,这使得它在这里无用

这些新信息不足以让我提供完整的答案,但它可能会让其他一些聪明的头脑得出一个包罗万象的结论。

2 个答案:

答案 0 :(得分:4)

此签名:

inline X operator+(X lhs, const X& rhs)

允许rvalues和lvalues作为操作的左侧。左值将被复制到lhs,xvalues将被移入lhs,而prvalues将被直接初始化为lhs

当我们链接多个lhs操作时,将lhs除以{和const&+之间的区别。我们来吧:

+====================+==============+=================+
|                    | X const& lhs | X lhs           |
+--------------------+--------------+-----------------+
| X sum = a+b;       | 1 copy       | 1 copy, 1 move  |
| X sum = X{}+b;     | 1 copy       | 1 move          |
| X sum = a+b+c;     | 2 copies     | 1 copy, 2 moves |
| X sum = X{}+b+c;   | 2 copies     | 2 moves         |
| X sum = a+b+c+d;   | 3 copies     | 1 copy, 3 moves |
| X sum = X{}+b+c+d; | 3 copies     | 3 moves         |
+====================+==============+=================+                

const&的第一个参数缩放份数。每个操作都是一个副本。通过值的第一个参数按比例缩放。每个操作只需一次移动,第一个参数是左值意味着额外的副本(或xvalues的附加移动)。

如果您的类型移动成本不高 - 对于移动和复制相同的情况 - 那么您希望通过const&获取第一个参数,因为它至少与其他情况一样好而且没有理由大惊小怪。

但如果移动成本更低,你实际上可能希望两个重载:

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

X operator+(X&& lhs, X const& rhs) {
    lhs += rhs;
    return std::move(lhs);
}

这将为所有中间临时对象使用移动而不是副本,但会在第一个上保存一次移动。不幸的是,最好的解决方案也是最冗长的。

答案 1 :(得分:0)

如果使用右值引用作为第一个参数调用运算符,例如当使用std :: move或函数调用的直接结果时,将调用lhs的移动构造函数。不需要额外的重载,它需要rvalue引用。