What is std::move(), and when should it be used?
Asked Answered
A

9

1130
  1. What is it?
  2. What does it do?
  3. When should it be used?

Good links are appreciated.

Apostatize answered 5/8, 2010 at 9:42 Comment(5)
Bjarne Stroustrup explains move in A Brief Introduction to Rvalue ReferencesOldline
Move semanticsApostatize
This question is referring to std::move(T && t); there also exists a std::move(InputIt first, InputIt last, OutputIt d_first) which is an algorithm related to std::copy. I point it out so others aren't as confused as I was when first confronted with a std::move taking three arguments. en.cppreference.com/w/cpp/algorithm/moveNorri
Recommend reading this if you don't have much of an idea what lvalue and rvalue references mean internalpointers.com/post/…Unbiased
To clarify, this question is about the std::move from <utility>, not the std::move from <algorithm>.Seersucker
B
426

Wikipedia Page on C++11 R-value references and move constructors

  1. In C++11, in addition to copy constructors, objects can have move constructors.
    (And in addition to copy assignment operators, they have move assignment operators.)
  2. The move constructor is used instead of the copy constructor, if the object has type "rvalue-reference" (Type &&).
  3. std::move() is a cast that produces an rvalue-reference to an object, to enable moving from it.

It's a new C++ way to avoid copies. For example, using a move constructor, a std::vector could just copy its internal pointer to data to the new object, leaving the moved object in an moved from state, therefore not copying all the data. This would be C++-valid.

Try googling for move semantics, rvalue, perfect forwarding.

Botulinus answered 5/8, 2010 at 9:52 Comment(11)
Move-semantics require the moved object remain valid, which is not an incorrect state. (Rationale: It still has to destruct, make it work.)Tenfold
@GMan: well, it has to be in a state that is safe to destruct, but, AFAIK, it does not have to be usable for anything else.Schumann
@ZanLynx: Right. Note that the standard library additionally requires moved objects be assignable, but this is only for objects used in the stdlib, not a general requirement.Tenfold
-1 "std::move() is the C++11 way to use move semantics" Please fix that. std::move() is not the way to use move semantics, move semantics are performed transparently to the programmer. move its only a cast to pass a value from one point to another where the original lvalue will no longer be used.Throughway
I'd go further. std::move itself does "nothing" - it has zero side effects. It just signals to the compiler that the programmer doesn't care what happens to that object any more. i.e. it gives permission to other parts of the software to move from the object, but it doesn't require that it be moved. In fact, the recipient of an rvalue reference doesn't have to make any promises about what it will or will not do with the data.Preparation
"The move constructor is used instead of the copy constructor, if the object has type "rvalue-reference"" Pardon the nitpicking, but expressions (any function argument is an expression rather than an object) can't have reference types. So rather than "has type "rvalue-reference"" a more precise wording would be "has "rvalue" value category" or something similar.Forbis
@Throughway Care to explain to me how what you just described isn't a literal move? That's literally what move is, moving out an lvalue, and after you move it, it's unusable past that... I don't care if std::move isn't the thing that does the moving. It IS a move. Don't believe me? Read it's name.Wordy
Oh how I lament the ridiculous things that C++ must invent to replace the simplicity of C! struct mystruct * newstruct = oldstruct; <== Look, I just moved the contents of oldstruct to newstruct in C by reassigning a pointer!Lindner
I do have a question I didn't find so far.an answer std::move does nothing. Ok, I've read this everywhere but returning a r-value reference means that you somehow underlined the fact that the move constructor (if any) should execute. If there is no move constructor, what would happen? Is something right from what I've written here?Bayles
I've been testing std::move() over and over with move constructor, it does nothing! The remaining r-value can be used arbitrarily, the behavior of move contructor depends totally on the implementation. I coudn't find a benefit on it yet.Sotos
I had read like 20 different explanations on std::move, and this one, in three simple lines, explained it perfectly.Mulley
B
592

1. "What is it?"

While std::move() is technically a function - I would say it isn't really a function. It's sort of a converter between ways the compiler considers an expression's value.

2. "What does it do?"

The first thing to note is that std::move() doesn't actually move anything. It changes an expression from being an lvalue (such as a named variable) to being an xvalue. An xvalue tells the compiler:

You can plunder me, move anything I'm holding and use it elsewhere (since I'm going to be destroyed soon anyway)".

in other words, when you use std::move(x), you're allowing the compiler to cannibalize x. Thus if x has, say, its own buffer in memory - after std::move()ing the compiler can have another object own it instead.

You can also move from a prvalue (such as a temporary you're passing around), but this is rarely useful.

3. "When should it be used?"

Another way to ask this question is "What would I cannibalize an existing object's resources for?" well, if you're writing application code, you would probably not be messing around a lot with temporary objects created by the compiler. So mainly you would do this in places like constructors, operator methods, standard-library-algorithm-like functions etc. where objects get created and destroyed automagically a lot. Of course, that's just a rule of thumb.

A typical use is 'moving' resources from one object to another instead of copying. @Guillaume links to this page which has a straightforward short example: swapping two objects with less copying.

template <class T>
swap(T& a, T& b) {
    T tmp(a);   // we've made a second copy of a
    a = b;      // we've made a second copy of b (and discarded a copy of a)
    b = tmp;    // we've made a second copy of tmp (and discarded a copy of b)
}

using move allows you to swap the resources instead of copying them around:

template <class T>
swap(T& a, T& b) {
    T tmp(std::move(a));
    a = std::move(b);   
    b = std::move(tmp);
}

Think of what happens when T is, say, vector<int> of size n. In the first version you read and write 3*n elements, in the second version you basically read and write just the 3 pointers to the vectors' buffers, plus the 3 buffers' sizes. Of course, class T needs to know how to do the moving; your class should have a move-assignment operator and a move-constructor for class T for this to work.

Bigamist answered 19/11, 2014 at 20:23 Comment(9)
For a long time I've heard of these move semantics, I never looked into them. From this description you've given it just seems like it's a shallow copy instead of a deep copy.Gristmill
@TitoneMaurice: Except that it's not a copy - as the original value is no longer usable.Bigamist
@Gristmill you couldn't be more wrong. A shallow copy leaves the original in the exact same state, a move usually results in the original being empty or in an otherwise valid state.Wahlstrom
@Wahlstrom Zebra isn't entirely wrong. While it's true that the original cannabilised object is usually deliberately sabotaged to avoid confusing errors (e.g. set its pointers to nullptr to signal that it no longer owns the pointees), the fact that the whole move is implemented by simply copying a pointer from the source to the destination (and deliberately avoiding doing anything with the pointee) is indeed reminiscent of a shallow copy. In fact, I would go so far as to say that a move is a shallow copy, followed optionally by a partial self-destruct of the source. (cont.)Rusticus
(cont.) If we permit this definition (and I rather like it), then @Zebrafish's observation isn't wrong, just slightly incomplete.Rusticus
@LightnessRacesinOrbit You're forgetting the implied ownership transfer of a move. If an owning object is moved from, it loses ownership, in shrill contrast with a shallow copy.Wahlstrom
@Wahlstrom Correct at a higher level of abstraction but if we're talking about how it's implemented then the fundamental similarities cannot be ignored, at least if our goal is to explain what C++ move semantics really are in practical terms. Even then, I believe the partial self-destruct of the source covers the loss of ownership angle.Rusticus
@rubenvb: This discussion seems to belong in chat (unless you think I should change my answer).Bigamist
I don't think your answer needs to be changed, my original comment here was directed at Zebrafish who has now morphed into Lightness Races In Orbit. I also can't seem to find the "move to chat" button that I vaguely remember being added to long comment discussions. @Lightness Anyhoo, I still think passing of a move operation as a glorified shallow copy is dangerous, especially to beginners. The optionality of the "self-destruct of the source" as you frame it depends highly on the ownership of the resources shallowly copied, and as such I don't think it can be labeled as optional in general.Wahlstrom
B
426

Wikipedia Page on C++11 R-value references and move constructors

  1. In C++11, in addition to copy constructors, objects can have move constructors.
    (And in addition to copy assignment operators, they have move assignment operators.)
  2. The move constructor is used instead of the copy constructor, if the object has type "rvalue-reference" (Type &&).
  3. std::move() is a cast that produces an rvalue-reference to an object, to enable moving from it.

It's a new C++ way to avoid copies. For example, using a move constructor, a std::vector could just copy its internal pointer to data to the new object, leaving the moved object in an moved from state, therefore not copying all the data. This would be C++-valid.

Try googling for move semantics, rvalue, perfect forwarding.

Botulinus answered 5/8, 2010 at 9:52 Comment(11)
Move-semantics require the moved object remain valid, which is not an incorrect state. (Rationale: It still has to destruct, make it work.)Tenfold
@GMan: well, it has to be in a state that is safe to destruct, but, AFAIK, it does not have to be usable for anything else.Schumann
@ZanLynx: Right. Note that the standard library additionally requires moved objects be assignable, but this is only for objects used in the stdlib, not a general requirement.Tenfold
-1 "std::move() is the C++11 way to use move semantics" Please fix that. std::move() is not the way to use move semantics, move semantics are performed transparently to the programmer. move its only a cast to pass a value from one point to another where the original lvalue will no longer be used.Throughway
I'd go further. std::move itself does "nothing" - it has zero side effects. It just signals to the compiler that the programmer doesn't care what happens to that object any more. i.e. it gives permission to other parts of the software to move from the object, but it doesn't require that it be moved. In fact, the recipient of an rvalue reference doesn't have to make any promises about what it will or will not do with the data.Preparation
"The move constructor is used instead of the copy constructor, if the object has type "rvalue-reference"" Pardon the nitpicking, but expressions (any function argument is an expression rather than an object) can't have reference types. So rather than "has type "rvalue-reference"" a more precise wording would be "has "rvalue" value category" or something similar.Forbis
@Throughway Care to explain to me how what you just described isn't a literal move? That's literally what move is, moving out an lvalue, and after you move it, it's unusable past that... I don't care if std::move isn't the thing that does the moving. It IS a move. Don't believe me? Read it's name.Wordy
Oh how I lament the ridiculous things that C++ must invent to replace the simplicity of C! struct mystruct * newstruct = oldstruct; <== Look, I just moved the contents of oldstruct to newstruct in C by reassigning a pointer!Lindner
I do have a question I didn't find so far.an answer std::move does nothing. Ok, I've read this everywhere but returning a r-value reference means that you somehow underlined the fact that the move constructor (if any) should execute. If there is no move constructor, what would happen? Is something right from what I've written here?Bayles
I've been testing std::move() over and over with move constructor, it does nothing! The remaining r-value can be used arbitrarily, the behavior of move contructor depends totally on the implementation. I coudn't find a benefit on it yet.Sotos
I had read like 20 different explanations on std::move, and this one, in three simple lines, explained it perfectly.Mulley
R
170

You can use move when you need to "transfer" the content of an object somewhere else, without doing a copy (i.e. the content is not duplicated, that's why it could be used on some non-copyable objects, like a unique_ptr). It's also possible for an object to take the content of a temporary object without doing a copy (and save a lot of time), with std::move.

This link really helped me out :

http://thbecker.net/articles/rvalue_references/section_01.html

I'm sorry if my answer is coming too late, but I was also looking for a good link for the std::move, and I found the links above a little bit "austere".

This puts the emphasis on r-value reference, in which context you should use them, and I think it's more detailed, that's why I wanted to share this link here.

Rodriguez answered 21/6, 2012 at 23:47 Comment(1)
Nice link. I always found the wikipedia article, and other links that I stumbled across rather confusing since they just throw facts at you, leaving it to you to figure out what the actual meaning/rationale is. While "move semantics" in a constructor is rather obvious, all those details about passing &&-values around are not... so the tutorial-style description was very nice.Vixen
A
143

Q: What is std::move?

A: std::move() is a function from the C++ Standard Library for casting to a rvalue reference.

Simplisticly std::move(t) is equivalent to:

static_cast<T&&>(t);

An rvalue is a temporary that does not persist beyond the expression that defines it, such as an intermediate function result which is never stored in a variable.

int a = 3; // 3 is a rvalue, does not exist after expression is evaluated
int b = a; // a is a lvalue, keeps existing after expression is evaluated

An implementation for std::move() is given in N2027: "A Brief Introduction to Rvalue References" as follows:

template <class T>
typename remove_reference<T>::type&&
std::move(T&& a)
{
    return a;
}

As you can see, std::move returns T&& no matter if called with a value (T), reference type (T&), or rvalue reference (T&&).

Q: What does it do?

A: As a cast, it does not do anything during runtime. It is only relevant at compile time to tell the compiler that you would like to continue considering the reference as an rvalue.

foo(3 * 5); // obviously, you are calling foo with a temporary (rvalue)

int a = 3 * 5;
foo(a);     // how to tell the compiler to treat `a` as an rvalue?
foo(std::move(a)); // will call `foo(int&& a)` rather than `foo(int a)` or `foo(int& a)`

What it does not do:

  • Make a copy of the argument
  • Call the copy constructor
  • Change the argument object

Q: When should it be used?

A: You should use std::move if you want to call functions that support move semantics with an argument which is not an rvalue (temporary expression).

This begs the following follow-up questions for me:

  • What is move semantics? Move semantics in contrast to copy semantics is a programming technique in which the members of an object are initialized by 'taking over' instead of copying another object's members. Such 'take over' makes only sense with pointers and resource handles, which can be cheaply transferred by copying the pointer or integer handle rather than the underlying data.

  • What kind of classes and objects support move semantics? It is up to you as a developer to implement move semantics in your own classes if these would benefit from transferring their members instead of copying them. Once you implement move semantics, you will directly benefit from work from many library programmers who have added support for handling classes with move semantics efficiently.

  • Why can't the compiler figure it out on its own? The compiler cannot just call another overload of a function unless you say so. You must help the compiler choose whether the regular or move version of the function should be called.

  • In which situations would I want to tell the compiler that it should treat a variable as an rvalue? This will most likely happen in template or library functions, where you know that an intermediate result could be salvaged (rather than allocating a new instance).

Antonetta answered 20/2, 2017 at 9:26 Comment(1)
Big +1 for code examples with semantics in comments. The other top answers define std::move using "move" itself - doesn't really clarify anything! --- I believe it's worth mentioning that not making a copy of the argument means that the original value cannot be reliably used.Autacoid
O
47

std::move itself doesn't really do much. I thought that it called the moved constructor for an object, but it really just performs a type cast (casting an lvalue variable to an rvalue so that the said variable can be passed as an argument to a move constructor or assignment operator).

So std::move is just used as a precursor to using move semantics. Move semantics is essentially an efficient way for dealing with temporary objects.

Consider Object A = B + (C + (D + (E + F)));

This is nice looking code, but E + F produces a temporary object. Then D + temp produces another temporary object and so on. In each normal "+" operator of a class, deep copies occur.

For example

Object Object::operator+ (const Object& rhs) {
    Object temp (*this);
    // logic for adding
    return temp;
}

The creation of the temporary object in this function is useless - these temporary objects will be deleted at the end of the line anyway as they go out of scope.

We can rather use move semantics to "plunder" the temporary objects and do something like

 Object& Object::operator+ (Object&& rhs) {
     // logic to modify rhs directly
     return rhs;
 }

This avoids needless deep copies being made. With reference to the example, the only part where deep copying occurs is now E + F. The rest uses move semantics. The move constructor or assignment operator also needs to be implemented to assign the result to A.

Outspeak answered 5/6, 2013 at 7:55 Comment(9)
you spoke about move semantics . you should add to your answer as how std::move can be used because the question asks about that.Tomboy
@Koushik std::move doesnt do much - but is used to implement move semantics. If you don't know about std::move, you probably dont know move semantics eitherOutspeak
"doesnt do much"(yes just a static_cast to to a rvalue reference). what actually does it do and y it does is what the OP asked. you need not know how std::move works but you got to know what move semantics does. furthermore, "but is used to implement move semantics" its the otherway around. know move semantics and you'l understand std::move otherwise no. move just helps in movement and itself uses move semantics. std::move does nothing but convert its argument to rvalue reference, which is what move semantics require.Tomboy
"but E + F produces a temporary object" - Operator + goes left to right, not right to left. Hence B+C would be first!Statuette
only your answer explained it to meTareyn
@Ajay: B+C cannot be first, look at the parentheses that specify the expected evaluation order and semantics, where E+F should be evaluated first and creating a temporary which, unless specified, does not have the move semantics (this temporary sum, call it "temp" is an rvalue, not an lvalue), this means that when evaluating F+temp, the operator should take 1st an lvalue (F) and 2nd an rvalue (temp), so it won't necessarily be the same overloaded "+" operator. If you don't specify the move semantics, the compiler may consider evaluating from left to right, not choosing the correct operator.Janiecejanifer
@Janiecejanifer The parentheses were added later! Please see the history of the answer! :)Statuette
Even with the parentheses (but without the std::move semantic explicitly stated), the compiler may still be allowed to change the evaluation order (notably if the operator is inlined, and it determines that it is associative and the order is not significant, which is not the case e.g. for floatting-point operations due to rounding of intermediate results, even for a basic "+" operator which is NOT associative, unlike with binary integers). Anyway, for floatting points there are other semantics that control the associativity. But parentheses do not strictly control the associativity.Janiecejanifer
Note as well that if the operators are not overloaded (to make a difference between implementations taking an lvalue or an rvalue), the std::move will have no effect: std:move is basically a hint sying to the compiler which optimizations it is allowed to do (such as reordering the evaluations, caching values into registers). Is is more powerful than "volatile" which should be avoided like the hell. "std::move" is often used to manage the lifecycle of object references with automated "delete" or "free", without using reference counters in many classes (including through exceptions handlers).Janiecejanifer
C
14

"What is it?" and "What does it do?" has been explained above.

I will give a example of "when it should be used".

For example, we have a class with lots of resource like big array in it.

class ResHeavy{ //  ResHeavy means heavy resource
    public:
        ResHeavy(int len=10):_upInt(new int[len]),_len(len){
            cout<<"default ctor"<<endl;
        }

        ResHeavy(const ResHeavy& rhs):_upInt(new int[rhs._len]),_len(rhs._len){
            cout<<"copy ctor"<<endl;
        }

        ResHeavy& operator=(const ResHeavy& rhs){
            _upInt.reset(new int[rhs._len]);
            _len = rhs._len;
            cout<<"operator= ctor"<<endl;
        }

        ResHeavy(ResHeavy&& rhs){
            _upInt = std::move(rhs._upInt);
            _len = rhs._len;
            rhs._len = 0;
            cout<<"move ctor"<<endl;
        }

    // check array valid
    bool is_up_valid(){
        return _upInt != nullptr;
    }

    private:
        std::unique_ptr<int[]> _upInt; // heavy array resource
        int _len; // length of int array
};

Test code:

void test_std_move2(){
    ResHeavy rh; // only one int[]
    // operator rh

    // after some operator of rh, it becomes no-use
    // transform it to other object
    ResHeavy rh2 = std::move(rh); // rh becomes invalid

    // show rh, rh2 it valid
    if(rh.is_up_valid())
        cout<<"rh valid"<<endl;
    else
        cout<<"rh invalid"<<endl;

    if(rh2.is_up_valid())
        cout<<"rh2 valid"<<endl;
    else
        cout<<"rh2 invalid"<<endl;

    // new ResHeavy object, created by copy ctor
    ResHeavy rh3(rh2);  // two copy of int[]

    if(rh3.is_up_valid())
        cout<<"rh3 valid"<<endl;
    else
        cout<<"rh3 invalid"<<endl;
}

output as below:

default ctor
move ctor
rh invalid
rh2 valid
copy ctor
rh3 valid

We can see that std::move with move constructor makes transform resource easily.

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. Previously, 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.

Cited:

Charlatanry answered 3/8, 2018 at 5:37 Comment(0)
S
2

std::move itself does nothing rather than a static_cast. According to cppreference.com

It is exactly equivalent to a static_cast to an rvalue reference type.

Thus, it depends on the type of the variable you assign to after the move, if the type has constructors or assign operators that takes a rvalue parameter, it may or may not steal the content of the original variable, so, it may leave the original variable to be in an unspecified state:

Unless otherwise specified, all standard library objects that have been moved from being placed in a valid but unspecified state.

Because there is no special move constructor or move assign operator for built-in literal types such as integers and raw pointers, so, it will be just a simple copy for these types.

Stlaurent answered 1/7, 2020 at 12:24 Comment(0)
M
0

std::move simply casts a variable to an rvalue reference. This rvalue reference is notated with &&. Let's say you have a class Foo and you instantiate an object like this

Foo foo = Foo();

If you then write

Foo foo2 = std::move(foo);

that's the same thing as If I wrote

Foo foo2 = (Foo&&) foo;

std::move replaces this cast to an rvalue reference. The reason why you would want to write either of the previous 2 lines of code is that if you write

Foo foo2 = foo;

The copy constructor will be called. Let's say Foo instances have a pointer to some data on the heap which they own. In Foo's destructor that data on the heap gets deleted. If you want to distinghuish between copying the data from the heap and taking ownership of that data, you can write a constructor which takes in const Foo& and that constructor can perform the deep copy. Then you can write a constructor which takes in an rvalue reference (Foo&&) and this constructor can simply rewire the pointers. This constructor which takes in Foo&& will be called when you write

Foo foo2 = std::move(foo);

and when you write

Foo foo2 = (Foo&&) foo;
Maxey answered 13/2, 2023 at 8:29 Comment(1)
I think it could be misleading using the C-style cast "(Foo&&)", since it provides little compile-time guarantee in contrast to using static_cast as already suggested by @ChristopherOezbeck. More info here. Otherwise this answer doesn't add much which hasn't already been covered.Kakapo
W
-1

Here is a full example, using std::move for a (simple) custom vector

Expected output:

 c: [10][11]
 copy ctor called
 copy of c: [10][11]
 move ctor called
 moved c: [10][11]

Compile as:

  g++ -std=c++2a -O2 -Wall -pedantic foo.cpp

Code:

#include <iostream>
#include <algorithm>

template<class T> class MyVector {
private:
    T *data;
    size_t maxlen;
    size_t currlen;
public:
    MyVector<T> () : data (nullptr), maxlen(0), currlen(0) { }
    MyVector<T> (int maxlen) : data (new T [maxlen]), maxlen(maxlen), currlen(0) { }

    MyVector<T> (const MyVector& o) {
        std::cout << "copy ctor called" << std::endl;
        data = new T [o.maxlen];
        maxlen = o.maxlen;
        currlen = o.currlen;
        std::copy(o.data, o.data + o.maxlen, data);
    }

    MyVector<T> (const MyVector<T>&& o) {
        std::cout << "move ctor called" << std::endl;
        data = o.data;
        maxlen = o.maxlen;
        currlen = o.currlen;
    }

    void push_back (const T& i) {
        if (currlen >= maxlen) {
            maxlen *= 2;
            auto newdata = new T [maxlen];
            std::copy(data, data + currlen, newdata);
            if (data) {
                delete[] data;
            }
            data = newdata;
        }
        data[currlen++] = i;
    }

    friend std::ostream& operator<<(std::ostream &os, const MyVector<T>& o) {
        auto s = o.data;
        auto e = o.data + o.currlen;;
        while (s < e) {
            os << "[" << *s << "]";
            s++;
        }
        return os;
    }
};

int main() {
    auto c = new MyVector<int>(1);
    c->push_back(10);
    c->push_back(11);
    std::cout << "c: " << *c << std::endl;
    auto d = *c;
    std::cout << "copy of c: " << d << std::endl;
    auto e = std::move(*c);
    delete c;
    std::cout << "moved c: " << e << std::endl;
}
Waers answered 13/5, 2020 at 19:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.