I've been looking into some of the new features of C++11 and one I've noticed is the double ampersand in declaring variables, like T&& var
.
For a start, what is this beast called? I wish Google would allow us to search for punctuation like this.
What exactly does it mean?
At first glance, it appears to be a double reference (like the C-style double pointers T** var
), but I'm having a hard time thinking of a use case for that.
Best Answer
It declares an rvalue reference (standards proposal doc).
Here's an introduction to rvalue references.
Here's a fantastic in-depth look at rvalue references by one of Microsoft's standard library developers.
The biggest difference between a C++03 reference (now called an lvalue reference in C++11) is that it can bind to an rvalue like a temporary without having to be const. Thus, this syntax is now legal:
rvalue references primarily provide for the following:
Move semantics. A move constructor and move assignment operator can now be defined that takes an rvalue reference instead of the usual const-lvalue reference. A move functions like a copy, except it is not obliged to keep the source unchanged; in fact, it usually modifies the source such that it no longer owns the moved resources. This is great for eliminating extraneous copies, especially in standard library implementations.
For example, a copy constructor might look like this:
If this constructor were passed a temporary, the copy would be unnecessary because we know the temporary will just be destroyed; why not make use of the resources the temporary already allocated? In C++03, there's no way to prevent the copy as we cannot determine whether we were passed a temporary. In C++11, we can overload a move constructor:
Notice the big difference here: the move constructor actually modifies its argument. This would effectively "move" the temporary into the object being constructed, thereby eliminating the unnecessary copy.
The move constructor would be used for temporaries and for non-const lvalue references that are explicitly converted to rvalue references using the
std::move
function (it just performs the conversion). The following code both invoke the move constructor forf1
andf2
:Perfect forwarding. rvalue references allow us to properly forward arguments for templated functions. Take for example this factory function:
If we called
factory<foo>(5)
, the argument will be deduced to beint&
, which will not bind to a literal 5, even iffoo
's constructor takes anint
. Well, we could instead useA1 const&
, but what iffoo
takes the constructor argument by non-const reference? To make a truly generic factory function, we would have to overload factory onA1&
and onA1 const&
. That might be fine if factory takes 1 parameter type, but each additional parameter type would multiply the necessary overload set by 2. That's very quickly unmaintainable.rvalue references fix this problem by allowing the standard library to define a
std::forward
function that can properly forward lvalue/rvalue references. For more information about howstd::forward
works, see this excellent answer.This enables us to define the factory function like this:
Now the argument's rvalue/lvalue-ness is preserved when passed to
T
's constructor. That means that if factory is called with an rvalue,T
's constructor is called with an rvalue. If factory is called with an lvalue,T
's constructor is called with an lvalue. The improved factory function works because of one special rule:Thus, we can use factory like so:
Important rvalue reference properties:
float f = 0f; int&& i = f;
is well formed because float is implicitly convertible to int; the reference would be to a temporary that is the result of the conversion.std::move
call is necessary in:foo&& r = foo(); foo f = std::move(r);