C++ Is a special rvalue-variant of an overloaded operator necessary?

Qqw*_*qwy 5 c++ operator-overloading rvalue rvalue-reference

In questions+answers like Operator Overloading, it is stated that the best way to overload a binary operator such as operator+ is:

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;
}
Run Code Online (Sandbox Code Playgroud)

operator+ itself thus takes lhs by value, rhs by const reference and returns the altered lhs by value.

I am having trouble understanding what would happen here if it would be called with an rvalue as lhs: Would this still be the single definition that is needed (and will the compiler optimize the movement of the argument and the return value), or does it make sense to add a second overloaded version of the operator that works with rvalue references?

EDIT:

Interestingly, in Boost.Operators, they talk about this implementation:

T operator+( const T& lhs, const T& rhs )
{
   T nrv( lhs );
   nrv += rhs;
   return nrv;
}
Run Code Online (Sandbox Code Playgroud)

which allows Named Return Value Optimization but it is not used by default because:

Sadly, not all compiler implement the NRVO, some even implement it in an incorrect way which makes it useless here

This new information is not enough for me to provide a full answer, but it might allow some other bright minds to derive at an encompassing conclusion.

Bar*_*rry 6

这个签名:

inline X operator+(X lhs, const X& rhs)
Run Code Online (Sandbox Code Playgroud)

允许右值和左值作为运算的左侧。lhs左值将被复制到 中,xvalues 将被移动到 中lhs,而纯右值将被直接初始化到 中lhs

服用之间的差异lhs按价值计算,采取lhsconst&物化当我们连锁多个+操作。让我们做一个表:

+====================+==============+=================+
|                    | 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         |
+====================+==============+=================+                
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

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