开发者

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

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&);
pr开发者_Python百科ivate:
    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!


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.


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?

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜