# C++ rvalue, && and Move

C++ is hard, the newer versions become even harder. This article will deal with some of the hard parts in C++, rvalue, rvalue reference (&&) and move semantics. And I am going to reverse engineer (not a metaphor) these complex and correlated topics, so you can understand them completely in one shot.

Firstly, let’s examine

# What is a rvalue?

A rvalue is one that should be on the right side of an equals sign.

Example:

Simple enough. Then let’s look at some more subtle rvalues, ones that are returned by functions:

Result:

It is worth noting that the way of returning a l-value (in the example) is considered a bad practice. So do not do that in real world programming.

# Beyond theoretical level

Whether a variable is a rvalue can make differences in real programming even before && is invented.

For example, this line

can be compiled fine while this:

generates following error:

The error message means that the compiler enforces a const reference for rvalue.

A more interesting example:

Result:

The difference is actually significant enough and compiler can determine overloaded functions.

# So rvalue is constant value?

Not exactly. And this where && (rvalue reference)comes in.

Example:

result:

If the functions are overloaded for rvalue, a rvalue variable choose the more specified version over the version takes a const reference parameter that is compatible for both. Thus, && can further diversify rvalue from const value.

In bellow I summarize the compatibility of overloaded function versions to different types in default setting. You can verify the result by selectively commenting out lines in the example above.

It sounds cool to further differentiate rvalue and constant value as they are not exactly the same indeed. But what is the practical value?

# What problem does && solve exactly?

The problem is the unnecessary deep copy when the argument is a rvalue.

To be more specific. && notation is provided to specify a rvalue, which can be used to avoid the deep copy when the rvalue, 1) is passed as an argument of either a constructor or an assignment operator, and 2) the class of which contains a pointer (or pointers) referring to dynamically allocated resource (memory).

It can be more specific with examples:

result:

The result are all good for the first two test cases, i.e., testCopy() and testAssign(), in which resource in res1 is copied for the res2. It is reasonable to copy the resource because they are two entities both need their unshared resource (a string).

However, in the third case, the (deep) copying of the resource in res1 is superfluous because the anonymous rvalue (returned by ResourceOwner(“res1”)) will be destructed right after the assignment thus it does not need the resource anymore:

I think it is a good chance to repeat the problem statement:

&& notation is provided to specify a rvalue, which can be used to avoid the deep copy when the rvalue, 1) is passed as an argument of either a constructor or an assignment operator, and 2) the class of which contains a pointer (or pointers) referring to dynamically allocated resource (memory).

If copying of a resource that is about to disappear is not optimal, what is the right operation then? The answer is

# Move

The idea is pretty straightforward, if the argument is a rvalue, we do not need to copy. Rather, we can simply “move” the resource (that is the memory the rvalue points to). Now let’s overload the assignment operator using the new technique:

This new assignment operator is called a move assignment operator. And a move constructor can be programmed in a similar way.

A good way of understanding this is: when you sell your old property and move to a new house, you do not have to toss all the furniture as we did in case 3 right? Rather, you can simply move the furniture to the new home.

All good.

# What is std::move?

Besides the move assignment operator and move constructor discussed above, there is one last missing piece in this puzzle, std::move.

Again, we look at the problem first:

when 1) we know a variable is in fact a rvalue, while 2) the compiler does not. The right version of the overloaded functions can not be called.

A common case is when we add another layer of resource owner, ResourceHolder and the relation of the three entities is given as bellow:

(N.b., in the following example, I complete the implementation of ResourceOwner’s move constructor as well)

Example:

In ResourceHolder’s move assignment operator, we want to call ResourceOwner’s move assignment operator since “a no-pointer member of a rvalue should be a rvalue too”. However, when we simply code resOwner = other.resOwner, what gets invoked is actually the ResourceOwner’s normal assignment operator that, again, incurs the extra copy.

It’s a good chance to repeat the problem statement again:

when 1) we know a variable is in fact a rvalue, while 2) the compiler does not. The right version of the overloaded functions can not be called.

As a solution we use to std::move to cast the variable to rvalue, so the right version of ResourceOwner’s assignment operator can be called.

# What is std::move exactly?

We know that type cast is not simply a compiler placebo telling a compiler that “I know what I am doing”. It effectively generate instructions of mov a value to bigger or smaller registers (e.g.,%eax->%cl) to conduct the “cast”.

So what std::move does exactly behind scene. I do not know myself when I am writing this paragraph, so let’s find out together.

First we modify the main a bit (I tried to make the style consistent)

Example:

Compile it, and dissemble the obj using

Result:

between the two nop, we can notice some dummy instructions generated for the move (if looking closely, you can know that they do basically nothing) However, if we turn on O (-O1) for the compiler, all the instructions will be gone.

Moreover, if changing the critical line to:

The assembly generated is identical.

That means the move semantics is pure syntax candy and a machine does not care at all.

To conclude,

The MACHINE thinks it irrelevant, we don’t.
-Harold Finch

That's it. If you have any suggestions, or you just like the read, , and send me your comments. Thank you for your time and attention. I'll catch you later.