
On Wed, Sep 10, 2008 at 6:17 PM, Michael Marcin <mike.marcin@gmail.com> wrote:
Peter Dimov wrote:
Michael Marcin: ...
It also allows you to support move assignment emulation rather elegantly using the move library in the sandbox.
There is no need to use a move library. The point of the above assignment is to take advantage of copy elision, which allows the compiler to construct the rvalue argument directly into 'arg', without a copy. In other words, if you have
For rvalues yes but for lvalues you need move emulation. Which can be implemented in the sandbox move library as follows.
class foo { public: foo(); foo( T t ); foo( boost::move_from<foo> other ) { swap( other.source ); }
foo& operator=( foo rhs ) { swap( rhs ); }
void swap();
//... };
T bar();
int main() { foo x; foo y( bar() ); x = boost::move(y); }
If you do not want to deal with move emulation (which I've found very brittle in complex expressions), a simple way to to gain the advantage of T::operator=(T rhs) even when assigning from lvalues is something like: template<class T> T destructive_copy(T& x) { using std::swap; T result; swap(result, x); return x; } which relies on NRVO to eliminate expensive copies: struct foo { foo(); foo(const& foo); foo& operator(foo); friend swap(foo&, foo&); }; foo t1; // no expensive copies if the compiler // does both NRVO and temporaries // elision foo t2 (destructive_copy(t2)); foo t3; // again, no expensive copies t3 = destructive_copy(t2); The name is also something that is easily greppable for (I do not use 'move' to prevent name collisions with an eventual std::move or boost::move). For types which do not have an optimized swap is suboptimal, so some sfinae trick on is_swappable might be needed. Ah, of course it requires T to be DefaultConstructible, and most importantly CopyConstructible so it doesn't handle move only types. HTH, -- gpd