这与此答案由马修M.如何利用与+运算符重载移动语义提供(一般来说,运营商不重新分配直接回左PARAM)。
他建议实行三种不同的重载:
inline T operator+(T left, T const& right) { left += right; return left; }
inline T operator+(T const& left, T right) { right += left; return right; } // commutative
inline T operator+(T left, T&& right) { left += right; return left; } // disambiguation
号码1和3是有意义的,但我不明白什么目的呢2。 该意见提出交换处理,但似乎1和2是相互排斥的(即实施歧义两个结果)
例如,在所有3个实现:
T a, b, c;
c = a + b;
编译器输出:
1> error C2593: 'operator +' is ambiguous
1> could be 'T operator +(const T &,T)'
1> or 'T operator +(T,const T &)'
1> while trying to match the argument list '(T, T)'
去除1或2个和节目按预期工作。 由于1是一般的情况和2只用可交换操作正常工作,我不明白为什么2都不会被使用。 是否有什么我失踪?
我不认为你错过任何东西 - 你的问题的代码确实是麻烦。 他的回答的前面部分是有道理的,但有什么东西在“四个所需的情况下”和实际的例子之间丢失。
这可能是更好的:
inline T operator+(T left, T const& right) { left += right; return left; }
inline T operator+(const T& left, T&& right) { right += left; return right; }
这实现该规则:使LHS的副本(优选通过移动结构)中,除RHS被到期无论如何,在这种情况下,修改它在适当位置。
对于非交换运营商,省略第二过载,否则提供不委托给复合赋值的实现。
如果你的类有嵌入到重量级的资源(以便它不能有效地移动),你要避免通过噪声值。 丹尼尔使得一些很好的意见在他的回答。 但不返回T&&
他建议,因为这是一个悬空的参考。
重要更新/警告这个答案!
这里实际上是一个有说服力的例子 ,其安静无声地创造合理的现实世界中的代码与下面悬空参考。 请使用其他答案的技术来避免,即使在被创造了一些额外的临时成本这个问题。 我会离开这个答案不变以供将来参考的其余部分。
对于可交换情况下,正确的重载方法:
T operator+( const T& lhs, const T& rhs )
{
T nrv( lhs );
nrv += rhs;
return nrv;
}
T&& operator+( T&& lhs, const T& rhs )
{
lhs += rhs;
return std::move( lhs );
}
T&& operator+( const T& lhs, T&& rhs )
{
rhs += lhs;
return std::move( rhs );
}
T&& operator+( T&& lhs, T&& rhs )
{
lhs += std::move( rhs );
return std::move( lhs );
}
这是为什么,它是如何工作的? 首先,请注意,如果你把右值引用作为参数,你可以修改和返回。 它从何而来需求的表达,以保证右值将不完整表达式的末尾,包括之前被销毁operator+
。 这也意味着, operator+
可以简单地返回右值引用作为呼叫者需要使用的结果operator+
(其是相同表达式的一部分)之前的表达被完全评估和临时对象(ravlues)是自毁。
第二个重要的观察是,如何节省了更多的临时和移动操作。 请看下面的表达式:
T a, b, c, d; // initialized somehow...
T r = a + b + c + d;
与上述,它是等效于:
T t( a ); // T operator+( const T& lhs, const T& rhs );
t += b; // ...part of the above...
t += c; // T&& operator+( T&& lhs, const T& rhs );
t += d; // T&& operator+( T&& lhs, const T& rhs );
T r( std::move( t ) ); // T&& was returned from the last operator+
比较这什么与其他方法发生了:
T t1( a ); // T operator+( T lhs, const T& rhs );
t1 += b; // ...part of the above...
T t2( std::move( t1 ) ); // t1 is an rvalue, so it is moved
t2 += c;
T t3( std::move( t2 ) );
t3 += d;
T r( std::move( t3 );
这意味着你还有3个临时工,尽管它们被移动而不是复制,上面的方法是完全避免的临时有效得多。
对于一个完整的图书馆,包括支持noexcept
,看到df.operators 。 在那里,你还可以找到混合类型的非交换情况下的版本和操作。
下面是一个完整的测试程序进行测试:
#include <iostream>
#include <utility>
struct A
{
A() { std::cout << "A::A()" << std::endl; }
A( const A& ) { std::cout << "A::A(const A&)" << std::endl; }
A( A&& ) { std::cout << "A::A(A&&)" << std::endl; }
~A() { std::cout << "A::~A()" << std::endl; }
A& operator+=( const A& ) { std::cout << "+=" << std::endl; return *this; }
};
// #define BY_VALUE
#ifdef BY_VALUE
A operator+( A lhs, const A& rhs )
{
lhs += rhs;
return lhs;
}
#else
A operator+( const A& lhs, const A& rhs )
{
A nrv( lhs );
nrv += rhs;
return nrv;
}
A&& operator+( A&& lhs, const A& rhs )
{
lhs += rhs;
return std::move( lhs );
}
#endif
int main()
{
A a, b, c, d;
A r = a + b + c + d;
}