Rvalue Reference Implications in C++11

3 minutes

There are several implications that one has to realize when it comes to using rvalue references in C++11 APIs. For starters, requiring an rvalue reference as a parameter to a function implies the following:

This is fairly large in terms of writing an API. In days of yore, before the existence of rvalues and move semantics, one might have used a pointer, or non-const reference to designate that an object would be mutated. They may have gone a step further and passed the object in by value to show that a deep copy would be made as the object passed in was no longer your concern. But none of these implied one very important feature that C++ technically lacks.

C99 added the restrict keyword. When applied to a pointer, the programmer is declaring to the compiler that for the lifetime of the pointer, only that pointer or a value derived from that pointer (such as pointer + some_offset) will access the data to which it points. This limits pointer aliasing, and helps the compiler to perform some cache optimizations.

C++ however does not have such a word. Yes, gcc and clang support the keyword being applied to references, but this is non-standard (and I should note, also sometimes breaks type traits and SFINAE if you expect a T const& but pass a T const& restrict) Now there are several proposals currently up for C++14 and beyond where we will be able to apply restrict as a modifier to types, and I’m all for that as forcing constant lvalue references to not refer to the same object is a good idea. But! Let us not foget the last implication of an rvalue reference (a properly moved one at that). Specifically:

The rvalue reference is declaring to the compiler for the lifetime of that object, or a value derived from the object (such as T const& = rvalue_reference) will access the data contained within the object.

In other words, an rvalue reference can declare to the compiler that the programmer intends for the object to only ever be accessed at on point.

Of course, this isn’t one hundred percent true, as an object that uses shallow copies internally would get around it. However, this also is also the same result as it would be in C99: Undefined Behavior. After all, declaring to the user that your API must fully and wholly own the object being passed in, and then magically mutating data contained within could be considered the same as casting away const on a reference, just so one may mutate it (which I should note is always 100% guaranteed to be undefined behavior, unless the original object it refers to was not declared const)

As far as I know, there are no compilers that will make assumptions or attempt to optimize based on this last implication. Whether they will or not also remains to be seen.