Object Swapping, Part 3: Swapping and Moving
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
- IDC Analyst Connection: Using Blade Systems to Cut Costs and Sharpen Efficiencies
- Application Testing Strategies in the IBM z/OS Environment
Reports
- Strategy: How to Conduct an Effective IT Security Risk Assessment
- Strategy: Smartphone Smackdown: Galaxy Note II vs. Lumia 920 vs. iPhone 5
Webcasts
- The Untapped Potential of Mobile Apps for Commercial Customers
- Why is Information Governance So Important for Modern Analytics?
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.

