hel*_*922 8 c++ move operator-overloading move-semantics c++11
这与Matthieu M.提供的关于如何利用+运算符重载(通常是不直接重新分配回左侧参数的运算符)一起使用移动语义的答案有关.
他建议实施三种不同的重载:
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
Run Code Online (Sandbox Code Playgroud)
1号和3号是有道理的,但我不明白2的目的是什么.评论建议交换处理,但似乎1和2是互斥的(即实现两个结果模糊)
例如,所有3个实现:
T a, b, c;
c = a + b;
Run Code Online (Sandbox Code Playgroud)
编译器输出:
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会被使用.有什么我想念的吗?
Ben*_*igt 12
我不认为你错过任何东西 - 你问题中的代码确实很麻烦.他的回答的早期部分是有道理的,但在"四个理想案例"和实际例子之间丢失了一些东西.
这可能会更好:
inline T operator+(T left, T const& right) { left += right; return left; }
inline T operator+(const T& left, T&& right) { right += left; return right; }
Run Code Online (Sandbox Code Playgroud)
这实现了规则:制作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 );
}
Run Code Online (Sandbox Code Playgroud)
为什么这样,它是如何工作的?首先,请注意,如果将rvalue引用作为参数,则可以修改并返回它.来自它的表达式需要保证在完整表达式结束之前不会破坏rvalue,包括operator+
.这也意味着operator+
可以简单地返回rvalue引用,因为调用者需要在表达式被完全评估之前使用结果operator+
(这是同一表达式的一部分)并且临时值(ravlues)被破坏.
第二个重要的观察是,这如何节省更多的临时工和移动操作.请考虑以下表达式:
T a, b, c, d; // initialized somehow...
T r = a + b + c + d;
Run Code Online (Sandbox Code Playgroud)
与上述相同,它相当于:
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+
Run Code Online (Sandbox Code Playgroud)
将此与其他方法的情况进行比较:
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 );
Run Code Online (Sandbox Code Playgroud)
这意味着你仍然有三个临时工,虽然他们被移动而不是复制,但上面的方法在完全避免临时工作方面更有效率.
有关完整的库,包括支持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;
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
3059 次 |
最近记录: |