I've just finished listening to the Software Engineering radio podcast interview with Scott Meyers regarding C++11. Most of the new features made sense to me, with the exception of one. I still don't get move semantics… What is it exactly?
C++ Move Semantics – Understanding Move Semantics in C++
c++c++-faqc++11move-semantics
Related Solutions
Think about what happens if T
is an lvalue reference, for example MyClass &
. In that case, T &&
would become MyClass & &&
, and due to reference collapsing rules, this would be transformed into MyClass &
again. To achieve the right result, typename remove_reference<MyClass&>::type&&
first removes any reference decorations from the type, so MyClass &
is mapped to MyClass
, and then the rvalue reference is applied to it, yielding MyClass &&
.
I thoroughly enjoyed all the answers and comments! And I agree with all of them. I just wanted to stick in one more motivation that no one has yet mentioned. This comes from N1377:
Move semantics is mostly about performance optimization: the ability to move an expensive object from one address in memory to another, while pilfering resources of the source in order to construct the target with minimum expense.
Move semantics already exists in the current language and library to a certain extent:
- copy constructor elision in some contexts
- auto_ptr "copy"
- list::splice
- swap on containers
All of these operations involve transferring resources from one object (location) to another (at least conceptually). What is lacking is uniform syntax and semantics to enable generic code to move arbitrary objects (just as generic code today can copy arbitrary objects). There are several places in the standard library that would greatly benefit from the ability to move objects instead of copy them (to be discussed in depth below).
I.e. in generic code such as vector::erase
, one needs a single unified syntax to move values to plug the hole left by the erased valued. One can't use swap
because that would be too expensive when the value_type
is int
. And one can't use copy assignment as that would be too expensive when value_type
is A
(the OP's A
). Well, one could use copy assignment, after all we did in C++98/03, but it is ridiculously expensive.
shouldn't all non-primitive type members be pointers to speed up movement
This would be horribly expensive when the member type is complex<double>
. Might as well color it Java.
Best Answer
I find it easiest to understand move semantics with example code. Let's start with a very simple string class which only holds a pointer to a heap-allocated block of memory:
Since we chose to manage the memory ourselves, we need to follow the rule of three. I am going to defer writing the assignment operator and only implement the destructor and the copy constructor for now:
The copy constructor defines what it means to copy string objects. The parameter
const string& that
binds to all expressions of type string which allows you to make copies in the following examples:Now comes the key insight into move semantics. Note that only in the first line where we copy
x
is this deep copy really necessary, because we might want to inspectx
later and would be very surprised ifx
had changed somehow. Did you notice how I just saidx
three times (four times if you include this sentence) and meant the exact same object every time? We call expressions such asx
"lvalues".The arguments in lines 2 and 3 are not lvalues, but rvalues, because the underlying string objects have no names, so the client has no way to inspect them again at a later point in time. rvalues denote temporary objects which are destroyed at the next semicolon (to be more precise: at the end of the full-expression that lexically contains the rvalue). This is important because during the initialization of
b
andc
, we could do whatever we wanted with the source string, and the client couldn't tell a difference!C++0x introduces a new mechanism called "rvalue reference" which, among other things, allows us to detect rvalue arguments via function overloading. All we have to do is write a constructor with an rvalue reference parameter. Inside that constructor we can do anything we want with the source, as long as we leave it in some valid state:
What have we done here? Instead of deeply copying the heap data, we have just copied the pointer and then set the original pointer to null (to prevent 'delete[]' from source object's destructor from releasing our 'just stolen data'). In effect, we have "stolen" the data that originally belonged to the source string. Again, the key insight is that under no circumstance could the client detect that the source had been modified. Since we don't really do a copy here, we call this constructor a "move constructor". Its job is to move resources from one object to another instead of copying them.
Congratulations, you now understand the basics of move semantics! Let's continue by implementing the assignment operator. If you're unfamiliar with the copy and swap idiom, learn it and come back, because it's an awesome C++ idiom related to exception safety.
Huh, that's it? "Where's the rvalue reference?" you might ask. "We don't need it here!" is my answer :)
Note that we pass the parameter
that
by value, sothat
has to be initialized just like any other string object. Exactly how isthat
going to be initialized? In the olden days of C++98, the answer would have been "by the copy constructor". In C++0x, the compiler chooses between the copy constructor and the move constructor based on whether the argument to the assignment operator is an lvalue or an rvalue.So if you say
a = b
, the copy constructor will initializethat
(because the expressionb
is an lvalue), and the assignment operator swaps the contents with a freshly created, deep copy. That is the very definition of the copy and swap idiom -- make a copy, swap the contents with the copy, and then get rid of the copy by leaving the scope. Nothing new here.But if you say
a = x + y
, the move constructor will initializethat
(because the expressionx + y
is an rvalue), so there is no deep copy involved, only an efficient move.that
is still an independent object from the argument, but its construction was trivial, since the heap data didn't have to be copied, just moved. It wasn't necessary to copy it becausex + y
is an rvalue, and again, it is okay to move from string objects denoted by rvalues.To summarize, the copy constructor makes a deep copy, because the source must remain untouched. The move constructor, on the other hand, can just copy the pointer and then set the pointer in the source to null. It is okay to "nullify" the source object in this manner, because the client has no way of inspecting the object again.
I hope this example got the main point across. There is a lot more to rvalue references and move semantics which I intentionally left out to keep it simple. If you want more details please see my supplementary answer.