Does it make sense to use the move-and-swap idiom on a movable and non-copied class

If I have a class like

class Foo{ public: Foo(){...} Foo(Foo && rhs){...} operator=(Foo rhs){ swap(*this, rhs);} void swap(Foo &rhs); private: Foo(const Foo&); // snip: swap code }; void swap(Foo& lhs, Foo& rhs); 

Does it make sense to implement operator = by value and swap if I don't have a copy constructor? This should prevent my Foo class objects from being copied, but allow movement.

This class is not copied, so I can not copy the construct or copy it.

Edit

I tested my code with this and it seems I like the behavior.

 #include <utility> #include <cstdlib> using std::swap; using std::move; class Foo{ public: Foo():a(rand()),b(rand()) {} Foo(Foo && rhs):a(rhs.a), b(rhs.b){rhs.a=rhs.b=-1;} Foo& operator=(Foo rhs){swap(*this,rhs);return *this;} friend void swap(Foo& lhs, Foo& rhs){swap(lhs.a,rhs.a);swap(lhs.b,rhs.b);} private: //My compiler doesn't yet implement deleted constructor Foo(const Foo&); private: int a, b; }; Foo make_foo() { //This is potentially much more complicated return Foo(); } int main(int, char*[]) { Foo f1; Foo f2 = make_foo(); //move-construct f1 = make_foo(); //move-assign f2 = move(f1); Foo f3(move(f2)); f2 = f3; // fails, can't copy-assign, this is wanted Foo f4(f3); // fails can't copy-construct return 0; } 
+4
source share
3 answers

Moving and sharing is really smart. If you turn off the copy constructor, then the only way you can call this function is to create an argument using the move constructor. That means if you write

 lhs = rhs; // Assume rhs is an rvalue 

Then the constructor of the operator = argument will be initialized by the move constructor, omitting rhs and setting the argument to the old rhs value. The swap call then exchanges the old lhs value and the rhs value, leaving lhs holding the rhs old value. Finally, the argument destructor lights up, clearing lhs old memory. As a side note, this really doesn't copy -and-swap the same way as moving -and-swap.

However, what you have now is wrong. Default implementation std::swap by default will try to use the move constructor to move elements around, resulting in an infinite recursion loop. You will need to overload std::swap for this to work correctly.

You can see it online here at ideone .

For more information, see this question and its discussion of the Four and a Half Rule.

Hope this helps!

+5
source

I think that everything is in order, but I really do not understand why you simply did not:

operator=(Foo&& rhs) // pass by rvalue reference not value

And save yourself a move.

+2
source

The following is an opinion, and I'm not very good at the 0x standard, but I think I have some pretty solid arguments supporting me.

No. In fact, it would be right not to support the appointment at all.

Consider the semantics:

"assign" means "reason B, which already exists, be identical to A". "copy" means "create B and make it identical to A". "swap" means "make B be identical to what A was, and at the same time make A be identical to what B". "move" means "make B be identical to what A was, and destroy A".

If we cannot copy, we cannot copy and replace. Copy-and-swap means a safe way to implement the assignment: we create a C that is identical to A, replace it with B (so C is now what B was, and B is identical to A) and destroys C (clear old data B) . It just doesn't work with move-and-swap: we should not destroy A at any moment, but this move will destroy it. In addition, the move does not create a new value, so what happens, we move A to B, and then there is nothing to change.

Also, the reason for creating the noncopyable class is, of course, not because "create B" will be problematic, but because "making it identical to A" will be problematic. IOW, if we cannot copy, why should we expect that we can assign?

+1
source

All Articles