C++11 r-value references

The C++03 standard treats temporary types as r-values (types only meant to go on the right hand side of an assignment expression). As such, it is only possible to bind a temporary to a const reference type. This is a somewhat arbitrary and, often, frustrating rule. The original idea was that there would be no good reason to modify a temporary; however, it turns out that there are plenty of good reasons for doing so and this arbitrary restriction was just a nuisance that served no good cause.

The C++11 standard recognises that there is no good reason to force this restriction on developers and so lifts it with the introduction of the r-value reference. The syntax is slightly different from a standard l-value reference in that it requires the use of two and not one ampersand, which is quite probably to make sure r-value references are not declared accidentally. In fact, the new standard does go to some lengths to ensure you don’t accidentally use an r-value reference. For example, a named type is never an r-value reference candidate and so to create one from it the std::move<t>() function template must be used.

Whilst not quite as seamless as it could be this is a useful change that will avoid, for example, the need to create stack based variables just to fulfil the arguments of a function call. I’ve lost track of the number of times I am calling a function that takes a non-const reference to a variable that it is going to change when I really don’t care about the outcome. The fact I still have to create a stack based object just to take this result is frustrating and makes for unclear code.

A useful side-effect of this new reference type is the fact it has allowed for the support of constructors with “move” rather than “copy” semantics. Prior to C++ 11, if you created an object in a local stack frame that you wanted to return to the called you had to return it by copy. This is, clearly, inefficient but necessary because the local object will go out of scope.

A lot of compilers do just that by implementing an optimisation called N/RVO ([named] return value optimisation). Unfortunately, it’s not part of the C++ standard and so you can’t rely on it. Also, compilers (and different versions of the same compiler) differ in how good they are at implementing this optimisation and so benefits can be inconsistent. Some implement full N/RVO, others only implement RVO and some implement neither!

The C++11 standard now recognises that a constructor that takes an r-value reference of the class type shall represent a constructor designed to implement move semantics. This basically means that instead of having to take a copy of all the contents of the old class to set up the new class you can just move the contents and that can be as simple as moving a pointer (assuming your class holds a pointer that points to all its data)

Consider, you are implementing a new type of vector container class. Before C++11 you would need to implement a copy-constructor that performed a deep-copy of that classes data (assuming you didn’t disable copying by hiding the c_c-tor). With a move constructor you can just copy the pointer that references the classes data and then set the pointer of the old class to nullptr (or NULL for those of us used to C++03) to indicate your new class has taken ownership of the resource.

In the case of returning something that is defined on the local stack frame, your move constructor will be called and all that does is copy the pointer to the recourse and not the resource itself. If the resource is a huge blob (technical term!) of memory this is the difference between copying a pointer and copying all that memory.

In C++, any type returned by value generates a temporary. In C++11 this will be treated as an r-value reference and as such the move constructor, if defined, will be called otherwise the copy-constructor is called. In C++ , where there is no such type as an r-value reference the standard behaviour of calling the copy-constructor will prevail.

In other words, to benefit from the more efficient move behaviour in C++11 there is no need to change any existing code other than to add a move constructor to your class. Once done C++ 11 compiled code will benefit from move semantics whilst C++03 compiled code will continue to behave the way it always has; implementing copy semantics.

Below are some examples r-value references in action.

#include <iostream>

using namespace std;

struct object
        cout << "default constructor" << endl;

    object(object const & o)
        cout << "copy constructor" << endl;

    object(object const && o)
        cout << "move constructor" << endl;

object foo()
    object o; // default constructor
    return o; // move constructor in C++11 and copy constructor in C++03

void bar(object & o)
   cout << "bar by l-value reference" << endl;

void bar(object && o)
   cout << "bar by r-value reference" << endl;

int main()
    object o = foo();              // constructs a new object via factory
    bar(o);                        // l-value reference
    bar(object());                 // r-value reference
    object & lvro = o;             // l-value reference
    object && rvro = std::move(o); // r-value reference from named type

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.