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.

At this point, it’s worth reiterating that std::move() gives a hint to the compiler that the programmer doesn’t need this object any more (at least, not in its current state). Consequently, you should not use std::move() on any persistent object you don’t want to modify, and you should not expect the state of any objects that have had std::move() applied to be the same after they are moved!

Move functions should always leave your objects in a well-defined state

As we noted in the previous lesson, it’s a good idea to always leave the objects being stolen from in some well-defined (deterministic) state. Ideally, this should be a “null state”, where the object is set back to its uninitiatized or zero state. Now we can talk about why: with std::move, the object being stolen from may not be a temporary after all. The user may want to reuse this (now empty) object again, or test it in some way, and can plan accordingly.

In the above example, string str is set to the empty string after being moved (which is what std::string always does after a successful move). This allows us to reuse variable str if we wish (or we can ignore it, if we no longer have a use for it).

Where else is std::move 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

48 comments to 15.4 — std::move

  • Gio

    Hi Alex,
    Can I use instead of std::move() a static_cast<T&&>?
    For example:

  • >> In C++11, std::move is a standard library function that serves a single purpose -- to convert its argument into an r-value.

    Alex,

    I'm curious. How can an l-value be converted to an r-value? I'd be very thankful if you can provide a toy example. With my below snippet, I get the error: Cannot bind rvalue reference of type 'int&&' to lvalue of type 'int'.

  • w4u

    v.push_back(str); // calls l-value version of push_back, which makes a copy
    is not correct, push_back take const reference, there is no copy involved.

    • nascardriver

      Hi w4u!

      The call itself doesn't create a copy, but our @std::vector stores @std::string elements, not references. It has to create a copy of the argument.

  • lpc

    "In the above example, vector str is set to the empty string after being moved (which is what std::vector always does after a successful move). This allows us to reuse the vector str if we wish (or we can ignore it, if we no longer have a use for it)."

    vector str? I think it should be string str here.

  • Isak

    Thanks Alex for the awesome tutorials so far! I have a question, can't std::move be used dangerously? Since we're moving our str in your program.  If we then plan to use str later on in the program, we can't since out string is moved! And iirc, that's UB right?

    Thanks in advance - Isak

    • nascardriver

      Hi Isak!

      You're right, this should really be included in this lesson. Certain functions of certain objects can be used after the object has been std::moved, but generally the object should not be used after having been std::moved.

      • Alex

        I'll make it explicit in the lesson, but this seems kind of obvious to me. 🙂 You're telling the compiler, "it's okay to steal the contents of this object". After doing so, why would you reasonably expect the contents to still be there?

        Any class that implements move semantics should leave the object being stolen from in some deterministic state (whether that state is useful is another question).

        • Isak

          Thank you Alex and nascardriver for your response. I think it's good idea to make it explicit in this tutorial to avoid any confusion.
          And what you're stating Alex makes complete sense, but still I think it's a good idea to mention it. Anyhow, keep coming with the tutorials and I hope we will see more computer science (data structures and algorithms) related stuff in a near future.

          - Isak

  • Steve

    My understanding is that the assigmment after std::move in fact doesn't change content, but only changes address in pointers. So in short, the move only works for variable [including blocky content] on heap, but is helpless for variable on stack. Am I correct?
    For arrays, vectors, the actual implementation is that they stores pointers allocated on stack pointing to data store in heap. When move, the data in heap experience no change at all, the location of pointers on stack also don't change, the only thing changed is the addresses stored in those pointers on stack. Am I right?

    • Alex

      Mostly correct (as far as I understand). A few minor nitpicks:
      1) For std::vector, the element data is allocated on the heap. So std::vector is always movable because the pointer to the elements can be transferred to another vector.
      2) For std::array, the element data is allocated on the stack. Now you might think this makes std::move useless, but it doesn't -- those elements might be classes that contain dynamic memory. That dynamic memory can be moved at least, which is better than making copies.

  • Advice

    Hi Alex,
    It might be worth mention when vector(of ur own object) resize it use copy constructor if not move constructor is noexcept?

  • Levon

    Hello Alex, for the swap function, how do we know that the class T will have a move constructor defined?

    thank you

  • David

    For the second example, Visual Studio is calling std::swap(x, y) instead of swap(x, y). Should it being doing that without the ‘std::’ prefix? I have not added "using namespace std" anywhere, and the compiler is in Debug mode. Is renaming the function the only way around this?

    This prints:

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

    • Alex

      I see the same thing with Visual Studio 2017, but I'm not sure why this is occurring. It only seems to occur when swapping strings. When I swap other data types, the user-defined swap is called as expected.

      You can force your user-defined version of swap to be called by calling ::swap instead of just swap.

      • David

        Thank you for the tip and looking into that for me. I was really thrown off. I thought the lack of "using namespace std" was keeping me safe from naming conflicts. I may have to start prefixing everything with a scope qualifier from now on, just to sleep at night.

  • 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 all code inside code tags: [code]your code here[/code]