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.
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;
}
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!