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

25 comments to 15.4 — std::move

  • Name

    Is it good idea to use std::move on every assignment ?
    And about templates, do you need to write template<…> before every function or class to use template or you can make one template on the top of the program like variable?

    • Alex

      > Is it good idea to use std::move on every assignment ?

      No, it’s only appropriate when you want to do a move, not a copy assignment.

      For templates, every function or class needs the template line.

  • I bet there should be

    in second example.
    Also, can you reply to my last e-mail?

  • Aitor

    Hi alex im having some troubles understanding one  little thing.
    when i use "std::move" to move  one ineger to another integer, and i print the first integer after move the resource from it , the value  of the integer seems to still there ,  the resource was not "stolen" from the integer as it should be, like in the string example. I put the code below:

    thank you.

    • Alex

      std::move just acts as a cast to an r-value reference. So when you do two = std::move(one), you’re just casting one to be an rvalue reference, which has no impact upon the assignment in this case. That’s why it acts identically to not using std::move.

      std::move is really meant to be used with classes, not fundamental types.

  • ali

    oh my god.
    it’s incrediably amazing!

    so alex, do you know, how are c# and java approach to this context? Do they select copy method or move method?

    • Alex

      I don’t know either Java or C# very well, so my answer may be incorrect. But my best understanding is that classes in java and C# both always use reference semantics for passing classes, so there’s little to be gained by move semantics (since there are no temporary copies being made).

  • ashwin

    Heey,

    I have a question about the vector example. Tell me if my thinking is right.
    Strings are dynamically allocated and that is why using std::move in push_back to prevent creating a copy makes sense. It doesn’t make sense to do a move for built in types like int and float as its not dynamically allocated. Am i right?

  • ashwin

    Hey,

    I have a doubt. In the first example where you swap two objects, what is the benefits of using a move constructor to copy constructor? ( for this statement  T tmp { std::move(a) }; )
    I am assuming the default generated copy and move constructors look like this:
    std::string(const string& rhs): buffer( new char[strlen(rhs)] ){ strcpy(buffer, rhs ); } //Copy ctor
    std::string(std::string&& rhs): buffer(rhs){ rhs = nullptr; } //Move coonstructor

    In copy constructor we are creating a new char* and in move we just transfer the ownership. Is that the only difference?

    • Alex

      Move constructors are much more efficient since we can just “transfer” the resource rather than making a copy.

      Think of it this way. You have an asparagus pie. You hate asparagus. Your friend loves asparagus pie, so you want him to have yours. What’s easier?
      1) Give your asparagus pie to your friend.
      2) Go bake a second asparagus pie, give that pie to your friend, and then throw your pie in the trash.

      #1 is a move. #2 is a copy.

  • Seamus Callaghan

    Can I use std::move to return a local std::vector from a function without copying it?

  • 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