I'm trying to dig deep into C++ and really learn the nuances of the language but one thing I've found to be really confusing is the R-Value reference. The whole double ampersand thing to be clear (in case I'm getting the terminology wrong). It's not a big deal as it doesn't seem to come up often but it seems like it could occasionally be useful to know.

3 Answers
3

A common use case is the “move constructor,” invoked when an object is being copied from a temporary that’s about to expire (a rvalue). An example is foo = bar + baz; where bar + baz is a temporary on the right-hand side of an assignment statement.

Before rvalue references, objects that made deep copies of their contents had to construct their data in one place, then copy it all to another, then destroy the first copy. This was slow and wasteful.

When the source is a rvalue reference, the move constructor or assignment operator will typically make a shallow copy of the source’s data, then remove the source’s references so that it will be an empty shell when it’s deleted a moment later and the data will now have a new owner. This is safe, because a rvalue reference won’t be used for anything else before it’s destroyed, much faster, and saves memory.

An interesting predecessor to this is that, when dealing with a function that returned an object, the compiler has been allowed to omit the copy function (i.e. it was implementation defined whether the copy occurred or not). Move mechanics using R-values is the natural extension of this, allowing for guaranteed moves (rather than implementation defined ones), allowing it in more circumstances, and allowing user-specified behaviors for it.
– Cort AmmonOct 9 '15 at 7:11

Really? You mean before C++11, it was copies everywhere? How did people write performance critical code back then?
– Winger SendonDec 21 '18 at 15:37

2

@WingerSendon Several ways, but one approach was a programmatic constructor. That is, you might express object foo = bar + baz; by constructing it in place as object foo( sum_constructor(), bar, baz ); with no copies.
– DavislorDec 21 '18 at 16:44

I think a little more detail is needed here. One can have return MyObject(); which appears to be a stack variable and therefor an lvalue. However, the compiler will normally turn it into an rvalue and either construct it in-place at the call site, or return it as a movable rvalue.
– user22815Oct 9 '15 at 17:15

I found this very hard to understand at first too. If you think about r-values as temporary, unnamed instances it helps. For example

functionCall( SomeObject{ 0 } );

In this case

SomeObject{ 0 }

is a constructor call creating a temporary, unnamed instance of the SomeObject class. If you make the signature of

functionCall( SomeObject&& x )

then it will only accept temporary unnamed objects.

Now, once you understand this, then you need to realize that a named, non-temporary object can be converted into an R-value either automatically by the compiler, or explicitly by the programmer ( std::move ).

If you move a named object std::move( x ) then x can no longer be used because it's "guts" (memory) have been moved.

The primary use for r-values as a language feature is to move objects into and out of functions without copying.