Search

15.4 — std::move

Once you start using move semantics more regularly, you’ll start to find cases where you want to invoke move semantics, but the objects you have to work with are l-values, not r-values. Consider the following swap function as an example:

Passed in two objects of type T (in this case, std::string), this function swaps their values by making three copies. Consequently, this program prints:

x: abc
y: de
x: de
y: abc

As we showed last lesson, making copies can be inefficient. And this version of swap makes 3 copies. That leads to a lot of excessive string creation and destruction, which is slow.

However, doing copies isn’t necessary here. All we’re really trying to do is swap the values of a and b, which can be accomplished just as well using 3 moves instead! So if we switch from copy semantics to move semantics, we can make our code more performant.

But how? The problem here is that parameters a and b are l-value references, not r-value references, so we don’t have a way to invoke the move constructor and move assignment operator instead of copy constructor and copy assignment. By default, we get the copy constructor and copy assignment behaviors. What are we to do?

std::move

In C++11, std::move is a standard library function that serves a single purpose -- to convert its argument into an r-value. We can pass an l-value to std::move, and it will return an r-value reference. std::move is defined in the utility header.

Here’s the same program as above, but with a swap() function that uses std::move to convert our l-values into r-values so we can invoke move semantics:

This prints the same result as above:

x: abc
y: de
x: de
y: abc

But it’s much more efficient about it. When tmp is initialized, instead of making a copy of x, we use std::move to convert l-value variable x into an r-value. Since the parameter is an r-value, move semantics are invoked, and x is moved into tmp.

With a couple of more swaps, the value of variable x has been moved to y, and the value of y has been moved to x.

Another example

We can also use std::move when filling elements of a container, such as std::vector, with l-values.

In the following program, we first add an element to a vector using copy semantics. Then we add an element to the vector using move semantics.

This program prints:

Copying str
str: Knock
vector: Knock

Moving str
str:
vector: Knock Knock

In the first case, we passed push_back() an l-value, so it used copy semantics to add an element to the vector. For this reason, the value in str is left alone.

In the second case, we passed push_back() an r-value (actually an l-value converted via std::move), so it used move semantics to add an element to the vector. This is more efficient, as the vector element can steal the string’s value rather than having to copy it. In this case, str is left empty.

Where else is this useful?

std::move can also be useful when sorting an array of elements. Many sorting algorithms (such as selection sort and bubble sort) work by swapping pairs of elements. In previous lessons, we’ve had to resort to copy-semantics to do the swapping. Now we can use move semantics, which is more efficient.

It can also be useful if we want to move the contents managed by one smart pointer to another.

Conclusion

std::move can be used whenever we want to treat an l-value like an r-value for the purpose of invoking move semantics instead of copy semantics.

15.5 -- std::unique_ptr
Index
15.3 -- Move constructors and move assignment

11 comments to 15.4 — std::move

  • apfelpektin

    in the first and second example the swap() in main uses variables a and b instead of the local x and y.

    in the second example also including the utility header is now redundant.

    i also wonder how the std::string instance str deals with the ‘stealing’. does it allocate a new (empty) string for itself or does it just keep a null pointer internally until something new is assigned to it or copied/moved from it?
    maybe it even doesn’t matter, since the function either will handle the move correctly if implemented, or just default to the copy semantics, so you don’t have to know if a class can handle move semantics?

    • Alex

      Thanks for pointing out the typos. Fixed!

      How std::string is implemented is compiler specific. The nice thing about classes is that you don’t need to worry about such things. As you note, if move semantics are implemented, they’ll get invoked transparently. If they aren’t, then copy semantics can be used.

  • Mauricio Mirabetti

    Also, If I may, I guess the phrase "And this version of std::swap makes 3 copies." refers to std::swap instead of this lesson specific version of swap function, which is what is being called. Perhaps std:: should be removed?

    Best regards.

  • aca4life

    two minor things:

    1) Just before section "std::move": "But how? … so we can’t don’t have…": unwanted doubled negative.

    2) Code in section "Another example": r21 should also output the message (but is redundant, r17 does effectively the same)

  • Vinayak

    15.4 std::move is not mentioned on the INDEX page or Home Page of http://www.learncpp.com/

Leave a Comment

Put C++ code inside [code][/code] tags to use the syntax highlighter