问题
If I have a class such as
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? It should prevent copying my objects of class Foo
but allow moves.
This class is not copyable so I shouldn't be able to copy construct or copy assign it.
Edit
I've tested my code with this and it seems to have the behaviour I want.
#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;
}
回答1:
Move-and-swap is indeed reasonable. If you disable the copy constructor, then the only way that you can invoke this function is if you were to construct the argument with the move constructor. This means that if you write
lhs = rhs; // Assume rhs is an rvalue
Then the constructor of the argument to operator =
will be initialized with the move constructor, emptying rhs
and setting the argument to the old value of rhs
. The call to swap
then exchanges lhs
's old value and rhs
's old value, leaving lhs
holding rhs
's old value. Finally, the destructor for the argument fires, cleaning up lhs
's old memory. As a note, this really isn't copy-and-swap as much as move-and-swap.
That said, what you have now isn't correct. The default implementation of std::swap
will internally try to use the move constructor to move the elements around, which results in an infinite recursion loop. You'd have to overload std::swap
to get this to work correctly.
You can see this online here at ideone.
For more information, see this question and its discussion of the "rule of four-and-a-half."
Hope this helps!
回答2:
I think this is fine, but I don't really understand why you wouldn't just do:
operator=(Foo&& rhs) // pass by rvalue reference not value
And save yourself a move.
回答3:
What follows is opinion, and I am not really up on the 0x standard, but I think I have fairly solid reasoning backing me up.
No. In fact, it would be proper not to support assignment at all.
Consider the semantics:
"assign" means "cause B, which exists already, to be identical to A". "copy" means "create B, and cause it to be identical to A". "swap" means "cause B to be identical to what A was, and simultaneously cause A to be identical to what B was". "move" means "cause B to be identical to what A was, and destroy A."
If we cannot copy, then we cannot copy-and-swap. Copy-and-swap is meant to be a safe way of implementing assignment: we create C which is identical to A, swap it with B (so that C is now what B was, and B is identical to A), and destroy C (cleaning up the old B data). This simply doesn't work with move-and-swap: we must not destroy A at any point, but the move will destroy it. Further, moving doesn't create a new value, so what happens is we move A into B, and then there is nothing to swap with.
Besides which - the reason for making the class noncopyable is surely not because "create B" will be problematic, but because "cause it to be identical to A" will be problematic. IOW, if we can't copy, why should we expect to be able to assign?
来源:https://stackoverflow.com/questions/6824043/does-it-make-sense-to-use-the-move-and-swap-idiom-on-a-movable-and-non-copyable