I’ve finally started to look into the new features in C++11 and I thought it would be useful to jot down the highlights, for myself or anyone else who’s curious. Since there’s a lot of ground to cover, I’m going to look at each item in its own post — this one covers move semantics.
This is the 1st of the 8 articles that currently make up the “C++11 Features” series.
As most C++ programmers will know, a new version of the standard was approved a couple of years ago, replacing the previous C++03. This is called C++11, and was formerly known as C++0x. Since I’ve recently happened across a few Stack Overflow questions which mentioned C++11 features I thought I’d have a look over the (to me, at least) more interesting ones and jot down the highlights here for anyone who’s interested.
This post covers move semantics.
The STL tends to be very clever at enabling fairly high-level functionality whilst minimising performance impact. One of its weakest areas, however, is the fact that one often wants to initialise containers from temporary values, or return a container from a function by value, and this involves a potentially expensive copy.
This issue has been improved in C++11 with the addition of move
constructors. These are the same as copy constructors in
essence, but they take a non-const
reference and are not required to leave
the source object in a valid state. They are used by the compiler in cases like
copying from a temporary value, where the source object is about to go out of
scope anyway and hence cannot be accessed after the operation.
This allows classes to implement more efficient copy constructors by having the
destination class take ownership of some underlying data directly instead of
having to copy it, similar in function to things like
std::vector::swap()
.
In C++03 this couldn’t work because references to temporary values could
only ever be const
:
1 2 3 4 5 6 7 8 9 10 11 |
|
To enable this behaviour in C++11 a new type of reference known as an
rvalue reference has been created. These may only be bound to rvalues (i.e.
temporary values) but they allow non-const
references to be bound. These are
specified using an extra ampersand as shown below:
1 2 3 4 5 6 7 8 9 10 11 |
|
If a function or method is overloaded with different variants which take rvalue and lvalue references then this allows code to behave more optimally when dealing with temporary values which can safely be invalidated. As well as the previously-mentioned move constructor it’s also possible to define move assignment operators in the same way.
The following trivial class which holds a block of memory shows the definition of both a move and copy constructor to illustrate how the move constructor is more efficient:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
|
In the example above it’s important to note how the buffer
pointer of the
source class gets reset to NULL
during the move constructor and assignment
operator. Without this, the destructor of the other class would delete
the
pointer still held by the destination class causing all sorts of mischief.
As a final note, named variables are never considered rvalues — only lvalue
references to them can be created. There are, however, cases where you may need
to treat an lvalue reference as an rvalue. In these instances the
std::move()
function can be used to “cast” an lvalue reference to
an rvalue version. Of course, careless use of this could cause all sorts of
problems, just as with casting.
For a more detailed discussion of move semantics, see this article.