Channels ▼

Andrew Koenig

Dr. Dobb's Bloggers

How the C++ Compiler Decides to Move Objects

July 10, 2013

Two weeks ago, I discussed rvalue references. I noted that, as its name suggests, an rvalue reference is a reference that is bound to an rvalue. More specifically, an rvalue reference can be bound only to an rvalue:

 
     int n = 3;
 
     int& r1 = 3;          // Error, 3 is an rvalue
     int& r2 = n;          // OK
     int&& r3 = 3;        // OK
     int&& r4 = n;        // Error, n is an lvalue

If we add const to the equation, both a plain reference and an rvalue reference can bind to an rvalue:

 
     const int&& r5 = 3;        // OK

However this binding involves adding a const to the type of 3. This addition turns out to be important for the following subtle reason:

 
     void foo(const int&);
     void foo(int&&);

Suppose we overload a function to accept a plain reference to const in one version and an rvalue reference in the other. Then whenever we call foo with an lvalue, we will get foo(const int&); whenever we call foo with an rvalue, we will get foo(int&&). The const is necessary for the plain reference because otherwise we would not be able to give foo a const int lvalue as its argument.

Inside the body of foo(const int&), its parameter is a reference to const. As usual, the argument is not copied, and the function itself cannot change the parameter's value because the parameter is const. The interesting part is what happens inside the body of foo(int&&).

Because an rvalue reference is just another kind of reference, the parameter of foo(int&&) is a reference. This reference is not a reference to const. In other words, the function is permitted to change its parameter's value, even though this value is an rvalue. For example:

 
     void foo(int&& n)
     {
           ++n;
     }

This function is permitted to increment n because n is a reference. It is permitted to do so even though we can call

 
    foo(3);

Here, 3 is an rvalue, so n gets bound to 3 without 3 being copied. Nevertheless, the function is permitted to change the value of n. Does doing so change the value of 3? The answer is that we can't tell, because there is no way of inspecting the value of 3 later on to discover whether it changed!

Now let's see how to apply this technique to defining a constructor:

 
               class Thing {
     public:
           // …
           Thing(const Thing& t) { /* … */ }
           Thing(Thing&& t) { /* … */ }
           // …
     };

We have defined a class Thing with two overloaded constructors. The first of them takes a reference to const Thing. Inside the body of this constructor, t refers directly to the object from which we want to construct our new Thing. As ever, we can use the contents of this object, but we cannot change those contents.

In the second constructor, t also refers to an object from which we are to construct a Thing. In this constructor, however, that object is known to be an rvalue — which means that we can change the contents of t without worrying about clobbering important data.

When we overload constructors in this way, we generally refer to the first one as the copy constructor and the second as the move constructor. Similarly, we can have copy-assignment and move-assignment operators as well:

 
     class Thing {
     public:
           // …
           Thing& operator=(const Thing& t) { /* … */ }
           Thing& operator=(Thing&& t) { /* … */ }
           // …
     };
 

For both constructors and assignment operators, the copy version is required to leave the original alone; the move version is permitted to destroy the original's value so long as the original object is left in a state that allows it to be safely destroyed.

Having seen this technique, you should now be able to figure out what std::move does, although not necessarily how it does it. Try to do so before we discuss it in detail next week.

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Comments:



Video