Channels ▼

Andrew Koenig

Dr. Dobb's Bloggers

Object Swapping, Part 3: Swapping and Moving

March 28, 2012

Last week, I showed how an algorithm can be made faster by swapping values instead of assigning them. I would like to continue by discussing the notion of moving, rather than copying, values.

More Insights

White Papers

More >>

Reports

More >>

Webcasts

More >>

Programs often copy objects and then throw the originals away. For example, this happens every time a function returns a local variable as its value: The variable is copied to wherever the function's caller intends to put the function's return value, and then the local variable is destroyed. If a function returns a string, vector, or other container, it is useful for the implementation to be able to avoid allocating a new copy of the container's contents and then immediately destroying the original container.

C++11 solves this problem by defining contexts in which the compiler can determine that an object that is about to be copied will soon be destroyed. In such contexts, the language lets the compiler call a move constructor or move assignment operator in place of the usual copy constructor or copy assignment operator. One such context is, of course, returning a local variable from a function. Another — which makes it easy to write programs that experiment with move operations — occurs when a program explicitly asks to move a value. A program can do so as follows:

 
     string s = "This is a string";
     string t = std::move(s);

The move function from the standard library, usually called explicitly as std::move to guard against conflicts with user-defined functions named move, signals to the compiler that the current value of s is not going to be used again. As a result, the initialization of t is permitted to call s's move constructor. If s does not have a move constructor, it will call s's copy constructor.

A move constructor is like a copy constructor, except that it is permitted to change the value of the source to any valid value. In other words, initializing t from std::move(s) is permitted to change the value of s, so long as s's value remains valid. It has to be valid because s will eventually be destroyed, and of course the destructor has to be able to run successfully. Moreover, it is completely legitimate for a program to assign a new value to s after moving its old value elsewhere. If that happens, the assignment operator has to be able to free s's current value before giving it a new one.

A container's move constructor will typically copy the container's internal state, including whatever pointers the container might have to the memory that holds the container's contents. It will then set those original pointers to zero or to some other value that will signal to the destructor that those pointers don't really point anywhere. As a consequence, after we execute

 
     string s = "This is a string";
     string t = std::move(s);

a likely value for s is a null string. However, it is part of the essence of moving that there is no guarantee that s is null; in fact, s could have any value.

Now let's look at an example from last week:

 
assert (n < vs.size());
for (size_t i = n+1; i != vs.size(); ++i)
           vs[i].swap(vs[i-1]);
vs.resize(vs.size()–1);

Recall that this example removes the nth element from a vector of strings, and that it works much more quickly than assignment because it doesn't need to copy and then discard most of the strings.

We can now rewrite this code fragment to move instead of swap:

 
assert (n < vs.size());
for (size_t i = n+1; i != vs.size(); ++i)
           vs[i-1] = std::move(vs[i]);     // Changed
vs.resize(vs.size()–1);
 

Each time through the loop, we move an element of vs into the previous element. When we're done with the loop, the last element of vs has a valid, but indeterminate, value; when we call resize, that value is destroyed.

Which of these two code fragments is better? I prefer the second, because it makes a clearer statement about the programmer's intentions. When we write

 
      vs[i].swap(vs[i-1]);

we are saying that when we're done, we want vs[i-1] to have vs[i]'s old value and vice versa. When we write

 
      vs[i-1] = std::move(vs[i]);
 

we are saying that we want vs[i-1] to have vs[i]'s old value, and we no longer care what value vs[i] has. In other words, for this algorithm, swapping instead of moving is overspecification.

We have said that when we move a value, we no longer care about the contents of our source. This indifference leads to a curious fact:

It is acceptable to implement move as swap.

If we do not care what value a variable has after we have moved from it, then we do not care whether the source takes on the former value of the destination. Therefore, there is nothing wrong with making a move assignment operator simply call swap, or for a move constructor to construct the default value of its object and then swap that value with its source.

This relationship between move and swap has interesting implications with respect to our strategy for implementing container classes. We shall start exploring those implications next week.

Related Reading






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.
 


Video