Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Move semantics - what it's all about? [duplicate]

Possible Duplicate:
Can someone please explain move semantics to me?

Could someone point me to a good source or explain it here what are the move semantics?

like image 955
smallB Avatar asked Jul 25 '11 11:07

smallB


2 Answers

Forget about C++0x for the moment. Move semantics are something that is language independent -- C++0x merely provides a standard way to perform operations with move semantics.

Definition

Move semantics define the behaviour of certain operations. Most of the time they are contrasted with copy semantics, so it would be useful to define them first.

Assignment with copy semantics has the following behaviour:

// Copy semantics
assert(b == c);
a = b;
assert(a == b && b == c);

i.e. a ends up equal to b, and we leave b unchanged.

Assignment with move semantics has weaker post conditions:

// Move semantics
assert(b == c);
move(a, b); // not C++0x
assert(a == c);

Note that there is no longer any guarantee that b remains unchanged after the assignment with move semantics. This is the crucial difference.

Uses

One benefit of move semantics is that it allows optimisations in certain situations. Consider the following regular value type:

struct A { T* x; };

Assume also that we define two objects of type A to be equal iff their x member point to equal values.

bool operator==(const A& lhs, const A& rhs) { return *lhs.x == *rhs.x; }

Finally assume that we define an object A to have sole ownership over the pointee of their x member.

A::~A() { delete x; }
A::A(const A& rhs) : x(new T(rhs.x)) {}
A& A::operator=(const A& rhs) { if (this != &rhs) *x = *rhs.x; }

Now suppose we want to define a function to swap two A objects.

We could do it the normal way with copy semantics.

void swap(A& a, A& b)
{
    A t = a;
    a = b;
    b = t;
}

However, this is unnecessarily inefficient. What are we doing?

  • We create a copy of a into t.
  • We then copy b into a.
  • Then copy t into b.
  • Finally, destroy t.

If T objects are expensive to copy then this is wasteful. If I asked you to swap two files on your computer, you wouldn't create a third file then copy and paste the file contents around before destroying your temporary file, would you? No, you'd move one file away, move the second into the first position, then finally move the first file back into the second. No need to copy data.

In our case, it's easy to move around objects of type A:

// Not C++0x
void move(A& lhs, A& rhs)
{
    lhs.x = rhs.x;
    rhs.x = nullptr;
}

We simply move rhs's pointer into lhs and then relinquish rhs ownership of that pointer (by setting it to null). This should illuminate why the weaker post condition of move semantics allows optimisations.

With this new move operation defined, we can define an optimised swap:

void swap(A& a, A& b)
{
    A t;
    move(t, a);
    move(a, b);
    move(b, t);
}

Another advantage of move semantics is that it allows you to move around objects that are unable to be copied. A prime example of this is std::auto_ptr.

C++0x

C++0x allows move semantics through its rvalue reference feature. Specifically, operations of the kind:

a = b;

Have move semantics when b is an rvalue reference (spelt T&&), otherwise they have copy semantics. You can force move semantics by using the std::move function (different from the move I defined earlier) when b is not an rvalue reference:

a = std::move(b);

std::move is a simple function that essentially casts its argument to an rvalue reference. Note that the results of expressions (such as a function call) are automatically rvalue references, so you can exploit move semantics in those cases without changing your code.

To define move optimisations, you need to define a move constructor and move assignment operator:

T::T(T&&);
T& operator=(T&&);

As these operations have move semantics, you are free to modify the arguments passed in (provided you leave the object in a destructible state).

Conclusion

That's essentially all there is to it. Note that rvalue references are also used to allow perfect forwarding in C++0x (due to the specifically crafted type system interactions between rvalue references and other types), but this isn't really related to move semantics, so I haven't discussed it here.

like image 53
Peter Alexander Avatar answered Oct 11 '22 13:10

Peter Alexander


Basically, rvalue references allow you to detect when objects are temporaries and you don't have to preserve their internal state. This allows for much more efficient code where C++03 used to have to copy all the time, in C++0x you can keep re-using the same resources. In addition, rvalue references enable perfect forwarding.

Have a look at this answer.

like image 28
Puppy Avatar answered Oct 11 '22 15:10

Puppy