Webcasts

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.

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.

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.

Video

This month's Dr. Dobb's Journal

This month,
Dr. Dobb's Journal is devoted to mobile programming. We introduce you to Apple's new Swift programming language, discuss the perils of being the third-most-popular mobile platform, revisit SQLite on Android
, and much more!