What is move semantics?
Asked Answered
B

12

2094

I've just finished listening to the Software Engineering radio podcast interview with Scott Meyers regarding C++11. Most of the new features made sense to me, with the exception of one. I still don't get move semantics... What is it exactly?

Breban answered 23/6, 2010 at 22:46 Comment(5)
I found [ Eli Bendersky's blog article](eli.thegreenplace.net/2011/12/15/…) about lvalues and rvalues in C and C++ pretty informative. He also mentions rvalue references in C++11 and introduces them with small examples.March
Alex Allain's exposition on the topic is very well written.Hartle
The only reference I can understand: learncpp.com/cpp-tutorial/…, i.e. the original reasoning of move semantics is from smart pointers.Psychopathology
See also stackoverflow.com/questions/3413470/…Poplar
If you're only starting out with advanced C++, here is an excellent talk straight from CppCon: youtu.be/7Qgd9B1KuMQBeefcake
T
2943

I find it easiest to understand move semantics with example code. Let's start with a very simple string class which only holds a pointer to a heap-allocated block of memory:

#include <cstring>
#include <algorithm>

class string
{
    char* data;

public:

    string(const char* p)
    {
        size_t size = std::strlen(p) + 1;
        data = new char[size];
        std::memcpy(data, p, size);
    }

Since we chose to manage the memory ourselves, we need to follow the rule of three. I am going to defer writing the assignment operator and only implement the destructor and the copy constructor for now:

    ~string()
    {
        delete[] data;
    }

    string(const string& that)
    {
        size_t size = std::strlen(that.data) + 1;
        data = new char[size];
        std::memcpy(data, that.data, size);
    }

The copy constructor defines what it means to copy string objects. The parameter const string& that binds to all expressions of type string which allows you to make copies in the following examples:

string a(x);                                    // Line 1
string b(x + y);                                // Line 2
string c(some_function_returning_a_string());   // Line 3

Now comes the key insight into move semantics. Note that only in the first line where we copy x is this deep copy really necessary, because we might want to inspect x later and would be very surprised if x had changed somehow. Did you notice how I just said x three times (four times if you include this sentence) and meant the exact same object every time? We call expressions such as x "lvalues".

The arguments in lines 2 and 3 are not lvalues, but rvalues, because the underlying string objects have no names, so the client has no way to inspect them again at a later point in time. rvalues denote temporary objects which are destroyed at the next semicolon (to be more precise: at the end of the full-expression that lexically contains the rvalue). This is important because during the initialization of b and c, we could do whatever we wanted with the source string, and the client couldn't tell a difference!

C++0x introduces a new mechanism called "rvalue reference" which, among other things, allows us to detect rvalue arguments via function overloading. All we have to do is write a constructor with an rvalue reference parameter. Inside that constructor we can do anything we want with the source, as long as we leave it in some valid state:

    string(string&& that)   // string&& is an rvalue reference to a string
    {
        data = that.data;
        that.data = nullptr;
    }

What have we done here? Instead of deeply copying the heap data, we have just copied the pointer and then set the original pointer to null (to prevent 'delete[]' from source object's destructor from releasing our 'just stolen data'). In effect, we have "stolen" the data that originally belonged to the source string. Again, the key insight is that under no circumstance could the client detect that the source had been modified. Since we don't really do a copy here, we call this constructor a "move constructor". Its job is to move resources from one object to another instead of copying them.

Congratulations, you now understand the basics of move semantics! Let's continue by implementing the assignment operator. If you're unfamiliar with the copy and swap idiom, learn it and come back, because it's an awesome C++ idiom related to exception safety.

    string& operator=(string that)
    {
        std::swap(data, that.data);
        return *this;
    }
};

Huh, that's it? "Where's the rvalue reference?" you might ask. "We don't need it here!" is my answer :)

Note that we pass the parameter that by value, so that has to be initialized just like any other string object. Exactly how is that going to be initialized? In the olden days of C++98, the answer would have been "by the copy constructor". In C++0x, the compiler chooses between the copy constructor and the move constructor based on whether the argument to the assignment operator is an lvalue or an rvalue.

So if you say a = b, the copy constructor will initialize that (because the expression b is an lvalue), and the assignment operator swaps the contents with a freshly created, deep copy. That is the very definition of the copy and swap idiom -- make a copy, swap the contents with the copy, and then get rid of the copy by leaving the scope. Nothing new here.

But if you say a = x + y, the move constructor will initialize that (because the expression x + y is an rvalue), so there is no deep copy involved, only an efficient move. that is still an independent object from the argument, but its construction was trivial, since the heap data didn't have to be copied, just moved. It wasn't necessary to copy it because x + y is an rvalue, and again, it is okay to move from string objects denoted by rvalues.

To summarize, the copy constructor makes a deep copy, because the source must remain untouched. The move constructor, on the other hand, can just copy the pointer and then set the pointer in the source to null. It is okay to "nullify" the source object in this manner, because the client has no way of inspecting the object again.

I hope this example got the main point across. There is a lot more to rvalue references and move semantics which I intentionally left out to keep it simple. If you want more details please see my supplementary answer.

Tena answered 24/6, 2010 at 12:40 Comment(60)
@But if my ctor is getting an rvalue, which can never be used later, why do I even need to bother leaving it in a consistent/safe state? Instead of setting that.data = 0, why not just leave it be?Upcoming
@Upcoming Because without that.data = 0, the characters would be destroyed way too early (when the temporary dies), and also twice. You want to steal the data, not share it!Tena
@Upcoming The regularly-scheduled destructor still gets run, so you have to ensure that the source object's post-move state doesn't cause a crash. Better, you should make sure the source object can also be the receiver of an assignment or other write.Laney
Your statement, "It is okay to "nullify" the source object in this manner, because the client has no way of inspecting the object again." Is it needed to nullify, because we are anyway not able to access it and if function defination ends, pointer will also gets destroyed?Dynamite
@pranitkothari Yes, all objects must be destructed, even moved-from objects. And since we do not want the char array to be deleted when that happens, we have to set the pointer to null.Tena
@Daan that's not correct. Well, you are right that the 'simple' operator shown is not a move assignment operator. It is a copy-assignment operator (that incidentally can also be used for moves). But the rest is not correct. As a simple counter example, consider a class foo with a unique_ptr member and another member of a type with a copy assignment, but no move assignment. Do you think foo will not have a move assignment operator? Also consider that built-in types don't have move assignment operators. Does that mean any class with an int member does not have a move assignment operator?Affectation
@Daan GCC does not behave like that. Clang does. I'll have to ask for a citation ;). (And no word about ints?)Affectation
Why you increased size of "that" string? - size_t size = strlen(that.data) + 1;Countermeasure
@Countermeasure Because strlen does not count the terminating NUL character.Tena
Why is this named move semantics? That name suggests that some object in memory is actually being moved (whatever that would mean) - but this is merely about transferring ownership of data.Thermolabile
@jco I agree. Transfer semantics would probably have been a better choice :)Tena
@FredOverflow I'm glad you agree, I was afraid I misunderstood something.Thermolabile
@FredOverflow what stops string from getting an implicit move assignment operator? That's the only thing confusing me atm.Lozengy
@Lozengy As soon as the programmer declares one of the special member functions (copy/move constructor, copy/move assignment, destructor) himself, none of them will be implicitly generated. And in this case, the implicitly generated move assignment operator would do the wrong thing anyway, because data = std::move(that.data) would merely copy the pointer. That's right, moving a pointer (or any other scalar data type) is defined to have the same effect as copying.Tena
thanks good explanation even though your copy ctr is not exception safe.Danonorwegian
@Danonorwegian What do you mean? Can memcpy throw an exception?Tena
@Tena no but new can.Danonorwegian
@Danonorwegian So? If new char[size] throws, where is the leak?Tena
@Tena But I did not say it has a leak.Danonorwegian
@Danonorwegian So what do you propose we do if new char[size] throws. What exactly is the problem, and how do we fix it?Tena
@Danonorwegian the copy ctor performs no destructive action, and no exceptions can be thrown after the only action that might need to be reversed completes (that is the allocation), so I don't see how it can not be exception safe.Affectation
@Tena this is probably does not cause any problem in your example, as size is not a member of the class, if it was then you have changed the size but not the data. My point was that its best to initialize data in the initialization list.Danonorwegian
@Andy The C++ standard specifically allows optimizing away the copy constructor!Tena
@Tena Oh, okay -- I'm sorry, I still find this confusing -- does that mean implementing a move constructor is not necessary in many cases? i.e. are you saying the copy constructor typically would be optimized away in the case of string a(b + c), even if there's no move constructor?Circum
@Circum Yes, compilers have been doing this for decades.Tena
@Tena okay. In that case, when would it actually be useful to write a move constructor? Since unless I misunderstand, a copy constructor would handle whatever case I can think of just as optimally?Circum
@Andy: Implement a move constructor when it is more efficient to move than to copy. In a complex object, a copy may be very deep and have a lot of overhead to it, whereas a move could be very trivial.Victorious
@RemyLebeau I understand there would be a cost to copying a deep object, but I'm still confused about what cases the copy constructor would not be optimized away (i.e. cases where a move constructor is necessary to improve performance)Circum
How does the compiler know when the source must remain untouched?Nozicka
So after stealing the buffer from the rvalue, the compiler will call its destructor at the end of the expression, causing a delete[] on nullptr. This doesn't make sense to me.Malar
@Malar What exactly doesn't make sense to you? Do you expect the compiler not to call the destructor at all? That would not be okay, because a moved-from object is still an object. Moving from an object does not end its lifetime.Tena
@Malar delete[] on a nullptr is defined by the C++ standard to be a no-op.Tena
So, we do not need to create a move assignment operator, after all?Seafowl
This example is good as well cppreferenceCollywobbles
Great example! I was going through your example step by step but I couldn't ever get the move constructor to be called. Is this b/c of copy elision?Omidyar
@Omidyar Yes, C++ compilers are very smart :) Are you using g++ or clang? Have you tried -fno-elide-constructors?Tena
@fredoverflow. I think I was using clang :/. I got it to work with that flag, but this was before I read another tutorial that mentioned you could coerce move semantics w/ std::move. Thanks!Omidyar
@Tena : Nice to see how operator= doesn't need to check for self assignment anymore.Prefix
I was browsing for ages to find a good explanation on this topic and your answer is by far the best I have read so for. Great Job!Retuse
@Retuse And I'm glad that even after 7 years, this answer still appears to be somewhat useful to my fellow programmers around the world :)Tena
In the move constructor , shouldn't we swap data and that.data instead of just setting that.data to null, because otherwise the data array prior to assignment won't ever be deleted?Sheet
@Sheet Are you confusing constructors and assignment operators? this.data has no deterministic value prior to construction, so reading from it during a swap would be UB.Tena
awesome explanation, I just have some trouble with "the client has no way of inspecting the object again" that you mention several times. The example here seems to suggest that it is completely ok to print a string after moving from itFructificative
Might it be better practice to memcpy(data, p, size * sizeof(char)); ? I know char is 1 byte and has been for a long time, but with internationalization and an interest in supporting more of the world's writing systems, it might not be that way for too long.Dropping
@formerlyknownas_463035818 "the client has no way of inspecting the object again" refers specifically to the values created by x + y and some_function_returning_a_string(), not moved-from strings or objects in general. Because those values are truly temporary, not stored anywhere, they go nowhere except straight into the copy constructor and can never be inspected by the client.Crawl
The answer provides a link to the copy and swap idiom. That link mentions that we cannot use std::swap to define a copy assignment because std::swap itself is defined in terms of the copy assignment. However, this answer is using std::swap. So which one is correct?Perfect
@Perfect We are swapping the char* pointers this->data and that.data, not the string objects *this and that. Assignment to char* pointers is not defined in terms of string object assignment.Tena
@fredoverflow, thank you for the answer but I couldn't follow it. I was asking about the use of std::swap in this post versus the statement (https://mcmap.net/q/15523/-what-is-the-copy-and-swap-idiom) that std::swap would not work. I am not quite seeing how what you point out related to that.Perfect
nice explanation, what happens when we don't manage our memories ourselves (e.g. do I need rvalue references, move semantics, etc if I derive from the string class above?)Gallicism
But wouldn't x + y be stored either in the stack or the .rodata section? What is the guarantee you can free that pointer in the destructor if such an implementation of the move constructor is used?Lowermost
@Lowermost The temporary x + y is stored on the stack, but the contained char* data points to heap memory, because we allocate it with new in the constructor.Tena
@Tena It is amazing that you are still responding to comments to this decade-old answer! Coming from C++98, I have a really dumb question... When an rvalue is encountered, instead of stealing the data from that, why couldn't this be an imposter and return that directly? Use that's memory address / pointer / reference as its own, replace the scope, ...Colo
@Tena WTF?! This is a GOAT level answer! You have some serious talent! You explained what several hours of C++Con videos and C++ books have failed to explain. You use a real world problem to explain rvalues as well as demonstrate its utility.Stifle
A lot of C++ concepts would be a lot easier to understand if the nomenclature was more accurate and familiar. Rvalue, as far as I understand it, are unassigned values. Just as you can have unassigned variables, the corollary is to have unassigned values. Technically, expressions and function bodies or blocks can be rvalues as well as their return values. Rvalue as a concept have been around forever, but we never bothered to deal with them until now. So rvalue as a concept is no longer an rvalue, since it's been given a name. LOL!Stifle
Hm, I'm wondering about the necessity of this move semantics. In this example, you could also avoid the copying by creating a constructor that takes a pointer. Then implement the ownership transfer ... this is useful ... but I'm not sure it is necesssary ... please prove me wrong?Muleteer
@YtsendeBoer You're confusing a desirable and undesirable copy. Using the example, a copy constructor creates a new object with an separate copy of its data. A desirable copy only performs a copy if the object's address has been assigned a "name" (lvalue), whereas an undesirable copy always performs a copy of the data (lvalue or rvalue). Since unassigned objects are temporary (rvalue), such as a return value or expression, it's far more efficient to perform a pointer assignment than to copy the data's contents, since it wasn't previously assigned a name (rvalue).Stifle
@YtsendeBoer Before modern C++, there were no facilities for detecting whether a value, such as one passed to a constructor, is an lvalue or rvalue. The example uses a copy constructor '&', and an overloaded counterpart '&&' for rvalues. The compiler calls the correct one based on whether it's rvalue or lvalue. A constructor which takes a pointer, as you said, is incapable of making this distinction. In a nutshell, Move semantics allows us to better optimize our code.Stifle
@Stifle Thanks, but in the example given in this answer, it is not necessary to rely on the compiler to detect whether a value is an rvalue or lvalue. The programmer can know this and explicitly call the proper constructor. I'm not trying to bash anything here. I'm just honestly trying to figure out if there are cases where you as a programmer cannot know runtime rvalue from lvalue at programming time and it is necessary to rely on the && semantics to avoid copying.Muleteer
@YtsendeBoer OK. Let's say have a function or method that accepts a string called print(string str);. You call it with string variable, print(my_string_var). You then call it again with a literal print("my string literal"), and again with a string return value from another function print(my_string_func()). For each of the three different arguments, which one is an lvalue and which one is an rvalue? Now, how would you implement print so it can tell an rvalue from a lvalue?Stifle
@Stifle Thanks for taking the time to answer. By now I've read the original proposal and played around with this move stuff a bit. I now appreciate your answer fully. The class developer did not have a tool before to "pilfer" the argument when it happens to be an rvalue and they have now. Thanks again and cheers!Muleteer
T
1276

My first answer was an extremely simplified introduction to move semantics, and many details were left out on purpose to keep it simple. However, there is a lot more to move semantics, and I thought it was time for a second answer to fill the gaps. The first answer is already quite old, and it did not feel right to simply replace it with a completely different text. I think it still serves well as a first introduction. But if you want to dig deeper, read on :)

Stephan T. Lavavej took the time to provide valuable feedback. Thank you very much, Stephan!

Introduction

Move semantics allows an object, under certain conditions, to take ownership of some other object's external resources. This is important in two ways:

  1. Turning expensive copies into cheap moves. See my first answer for an example. Note that if an object does not manage at least one external resource (either directly, or indirectly through its member objects), move semantics will not offer any advantages over copy semantics. In that case, copying an object and moving an object means the exact same thing:

    class cannot_benefit_from_move_semantics
    {
        int a;        // moving an int means copying an int
        float b;      // moving a float means copying a float
        double c;     // moving a double means copying a double
        char d[64];   // moving a char array means copying a char array
    
        // ...
    };
    
  2. Implementing safe "move-only" types; that is, types for which copying does not make sense, but moving does. Examples include locks, file handles, and smart pointers with unique ownership semantics. Note: This answer discusses std::auto_ptr, a deprecated C++98 standard library template, which was replaced by std::unique_ptr in C++11. Intermediate C++ programmers are probably at least somewhat familiar with std::auto_ptr, and because of the "move semantics" it displays, it seems like a good starting point for discussing move semantics in C++11. YMMV.

What is a move?

The C++98 standard library offers a smart pointer with unique ownership semantics called std::auto_ptr<T>. In case you are unfamiliar with auto_ptr, its purpose is to guarantee that a dynamically allocated object is always released, even in the face of exceptions:

{
    std::auto_ptr<Shape> a(new Triangle);
    // ...
    // arbitrary code, could throw exceptions
    // ...
}   // <--- when a goes out of scope, the triangle is deleted automatically

The unusual thing about auto_ptr is its "copying" behavior:

auto_ptr<Shape> a(new Triangle);

      +---------------+
      | triangle data |
      +---------------+
        ^
        |
        |
        |
  +-----|---+
  |   +-|-+ |
a | p | | | |
  |   +---+ |
  +---------+

auto_ptr<Shape> b(a);

      +---------------+
      | triangle data |
      +---------------+
        ^
        |
        +----------------------+
                               |
  +---------+            +-----|---+
  |   +---+ |            |   +-|-+ |
a | p |   | |          b | p | | | |
  |   +---+ |            |   +---+ |
  +---------+            +---------+

Note how the initialization of b with a does not copy the triangle, but instead transfers the ownership of the triangle from a to b. We also say "a is moved into b" or "the triangle is moved from a to b". This may sound confusing because the triangle itself always stays at the same place in memory.

To move an object means to transfer ownership of some resource it manages to another object.

The copy constructor of auto_ptr probably looks something like this (somewhat simplified):

auto_ptr(auto_ptr& source)   // note the missing const
{
    p = source.p;
    source.p = 0;   // now the source no longer owns the object
}

Dangerous and harmless moves

The dangerous thing about auto_ptr is that what syntactically looks like a copy is actually a move. Trying to call a member function on a moved-from auto_ptr will invoke undefined behavior, so you have to be very careful not to use an auto_ptr after it has been moved from:

auto_ptr<Shape> a(new Triangle);   // create triangle
auto_ptr<Shape> b(a);              // move a into b
double area = a->area();           // undefined behavior

But auto_ptr is not always dangerous. Factory functions are a perfectly fine use case for auto_ptr:

auto_ptr<Shape> make_triangle()
{
    return auto_ptr<Shape>(new Triangle);
}

auto_ptr<Shape> c(make_triangle());      // move temporary into c
double area = make_triangle()->area();   // perfectly safe

Note how both examples follow the same syntactic pattern:

auto_ptr<Shape> variable(expression);
double area = expression->area();

And yet, one of them invokes undefined behavior, whereas the other one does not. So what is the difference between the expressions a and make_triangle()? Aren't they both of the same type? Indeed they are, but they have different value categories.

Value categories

Obviously, there must be some profound difference between the expression a which denotes an auto_ptr variable, and the expression make_triangle() which denotes the call of a function that returns an auto_ptr by value, thus creating a fresh temporary auto_ptr object every time it is called. a is an example of an lvalue, whereas make_triangle() is an example of an rvalue.

Moving from lvalues such as a is dangerous, because we could later try to call a member function via a, invoking undefined behavior. On the other hand, moving from rvalues such as make_triangle() is perfectly safe, because after the copy constructor has done its job, we cannot use the temporary again. There is no expression that denotes said temporary; if we simply write make_triangle() again, we get a different temporary. In fact, the moved-from temporary is already gone on the next line:

auto_ptr<Shape> c(make_triangle());
                                  ^ the moved-from temporary dies right here

Note that the letters l and r have a historic origin in the left-hand side and right-hand side of an assignment. This is no longer true in C++, because there are lvalues that cannot appear on the left-hand side of an assignment (like arrays or user-defined types without an assignment operator), and there are rvalues which can (all rvalues of class types with an assignment operator).

An rvalue of class type is an expression whose evaluation creates a temporary object. Under normal circumstances, no other expression inside the same scope denotes the same temporary object.

Rvalue references

We now understand that moving from lvalues is potentially dangerous, but moving from rvalues is harmless. If C++ had language support to distinguish lvalue arguments from rvalue arguments, we could either completely forbid moving from lvalues, or at least make moving from lvalues explicit at call site, so that we no longer move by accident.

C++11's answer to this problem is rvalue references. An rvalue reference is a new kind of reference that only binds to rvalues, and the syntax is X&&. The good old reference X& is now known as an lvalue reference. (Note that X&& is not a reference to a reference; there is no such thing in C++.)

If we throw const into the mix, we already have four different kinds of references. What kinds of expressions of type X can they bind to?

            lvalue   const lvalue   rvalue   const rvalue
---------------------------------------------------------              
X&          yes
const X&    yes      yes            yes      yes
X&&                                 yes
const X&&                           yes      yes

In practice, you can forget about const X&&. Being restricted to read from rvalues is not very useful.

An rvalue reference X&& is a new kind of reference that only binds to rvalues.

Implicit conversions

Rvalue references went through several versions. Since version 2.1, an rvalue reference X&& also binds to all value categories of a different type Y, provided there is an implicit conversion from Y to X. In that case, a temporary of type X is created, and the rvalue reference is bound to that temporary:

void some_function(std::string&& r);

some_function("hello world");

In the above example, "hello world" is an lvalue of type const char[12]. Since there is an implicit conversion from const char[12] through const char* to std::string, a temporary of type std::string is created, and r is bound to that temporary. This is one of the cases where the distinction between rvalues (expressions) and temporaries (objects) is a bit blurry.

Move constructors

A useful example of a function with an X&& parameter is the move constructor X::X(X&& source). Its purpose is to transfer ownership of the managed resource from the source into the current object.

In C++11, std::auto_ptr<T> has been replaced by std::unique_ptr<T> which takes advantage of rvalue references. I will develop and discuss a simplified version of unique_ptr. First, we encapsulate a raw pointer and overload the operators -> and *, so our class feels like a pointer:

template<typename T>
class unique_ptr
{
    T* ptr;

public:

    T* operator->() const
    {
        return ptr;
    }

    T& operator*() const
    {
        return *ptr;
    }

The constructor takes ownership of the object, and the destructor deletes it:

    explicit unique_ptr(T* p = nullptr)
    {
        ptr = p;
    }

    ~unique_ptr()
    {
        delete ptr;
    }

Now comes the interesting part, the move constructor:

    unique_ptr(unique_ptr&& source)   // note the rvalue reference
    {
        ptr = source.ptr;
        source.ptr = nullptr;
    }

This move constructor does exactly what the auto_ptr copy constructor did, but it can only be supplied with rvalues:

unique_ptr<Shape> a(new Triangle);
unique_ptr<Shape> b(a);                 // error
unique_ptr<Shape> c(make_triangle());   // okay

The second line fails to compile, because a is an lvalue, but the parameter unique_ptr&& source can only be bound to rvalues. This is exactly what we wanted; dangerous moves should never be implicit. The third line compiles just fine, because make_triangle() is an rvalue. The move constructor will transfer ownership from the temporary to c. Again, this is exactly what we wanted.

The move constructor transfers ownership of a managed resource into the current object.

Move assignment operators

The last missing piece is the move assignment operator. Its job is to release the old resource and acquire the new resource from its argument:

    unique_ptr& operator=(unique_ptr&& source)   // note the rvalue reference
    {
        if (this != &source)    // beware of self-assignment
        {
            delete ptr;         // release the old resource

            ptr = source.ptr;   // acquire the new resource
            source.ptr = nullptr;
        }
        return *this;
    }
};

Note how this implementation of the move assignment operator duplicates logic of both the destructor and the move constructor. Are you familiar with the copy-and-swap idiom? It can also be applied to move semantics as the move-and-swap idiom:

    unique_ptr& operator=(unique_ptr source)   // note the missing reference
    {
        std::swap(ptr, source.ptr);
        return *this;
    }
};

Now that source is a variable of type unique_ptr, it will be initialized by the move constructor; that is, the argument will be moved into the parameter. The argument is still required to be an rvalue, because the move constructor itself has an rvalue reference parameter. When control flow reaches the closing brace of operator=, source goes out of scope, releasing the old resource automatically.

The move assignment operator transfers ownership of a managed resource into the current object, releasing the old resource. The move-and-swap idiom simplifies the implementation.

Moving from lvalues

Sometimes, we want to move from lvalues. That is, sometimes we want the compiler to treat an lvalue as if it were an rvalue, so it can invoke the move constructor, even though it could be potentially unsafe. For this purpose, C++11 offers a standard library function template called std::move inside the header <utility>. This name is a bit unfortunate, because std::move simply casts an lvalue to an rvalue; it does not move anything by itself. It merely enables moving. Maybe it should have been named std::cast_to_rvalue or std::enable_move, but we are stuck with the name by now.

Here is how you explicitly move from an lvalue:

unique_ptr<Shape> a(new Triangle);
unique_ptr<Shape> b(a);              // still an error
unique_ptr<Shape> c(std::move(a));   // okay

Note that after the third line, a no longer owns a triangle. That's okay, because by explicitly writing std::move(a), we made our intentions clear: "Dear constructor, do whatever you want with a in order to initialize c; I don't care about a anymore. Feel free to have your way with a."

std::move(some_lvalue) casts an lvalue to an rvalue, thus enabling a subsequent move.

Xvalues

Note that even though std::move(a) is an rvalue, its evaluation does not create a temporary object. This conundrum forced the committee to introduce a third value category. Something that can be bound to an rvalue reference, even though it is not an rvalue in the traditional sense, is called an xvalue (eXpiring value). The traditional rvalues were renamed to prvalues (Pure rvalues).

Both prvalues and xvalues are rvalues. Xvalues and lvalues are both glvalues (Generalized lvalues). The relationships are easier to grasp with a diagram:

        expressions
          /     \
         /       \
        /         \
    glvalues   rvalues
      /  \       /  \
     /    \     /    \
    /      \   /      \
lvalues   xvalues   prvalues

Note that only xvalues are really new; the rest is just due to renaming and grouping.

C++98 rvalues are known as prvalues in C++11. Mentally replace all occurrences of "rvalue" in the preceding paragraphs with "prvalue".

Moving out of functions

So far, we have seen movement into local variables, and into function parameters. But moving is also possible in the opposite direction. If a function returns by value, some object at call site (probably a local variable or a temporary, but could be any kind of object) is initialized with the expression after the return statement as an argument to the move constructor:

unique_ptr<Shape> make_triangle()
{
    return unique_ptr<Shape>(new Triangle);
}          \-----------------------------/
                  |
                  | temporary is moved into c
                  |
                  v
unique_ptr<Shape> c(make_triangle());

Perhaps surprisingly, automatic objects (local variables that are not declared as static) can also be implicitly moved out of functions:

unique_ptr<Shape> make_square()
{
    unique_ptr<Shape> result(new Square);
    return result;   // note the missing std::move
}

How come the move constructor accepts the lvalue result as an argument? The scope of result is about to end, and it will be destroyed during stack unwinding. Nobody could possibly complain afterward that result had changed somehow; when control flow is back at the caller, result does not exist anymore! For that reason, C++11 has a special rule that allows returning automatic objects from functions without having to write std::move. In fact, you should never use std::move to move automatic objects out of functions, as this inhibits the "named return value optimization" (NRVO).

Never use std::move to move automatic objects out of functions.

Note that in both factory functions, the return type is a value, not an rvalue reference. Rvalue references are still references, and as always, you should never return a reference to an automatic object; the caller would end up with a dangling reference if you tricked the compiler into accepting your code, like this:

unique_ptr<Shape>&& flawed_attempt()   // DO NOT DO THIS!
{
    unique_ptr<Shape> very_bad_idea(new Square);
    return std::move(very_bad_idea);   // WRONG!
}

Never return automatic objects by rvalue reference. Moving is exclusively performed by the move constructor, not by std::move, and not by merely binding an rvalue to an rvalue reference.

Moving into members

Sooner or later, you are going to write code like this:

class Foo
{
    unique_ptr<Shape> member;

public:

    Foo(unique_ptr<Shape>&& parameter)
    : member(parameter)   // error
    {}
};

Basically, the compiler will complain that parameter is an lvalue. If you look at its type, you see an rvalue reference, but an rvalue reference simply means "a reference that is bound to an rvalue"; it does not mean that the reference itself is an rvalue! Indeed, parameter is just an ordinary variable with a name. You can use parameter as often as you like inside the body of the constructor, and it always denotes the same object. Implicitly moving from it would be dangerous, hence the language forbids it.

A named rvalue reference is an lvalue, just like any other variable.

The solution is to manually enable the move:

class Foo
{
    unique_ptr<Shape> member;

public:

    Foo(unique_ptr<Shape>&& parameter)
    : member(std::move(parameter))   // note the std::move
    {}
};

You could argue that parameter is not used anymore after the initialization of member. Why is there no special rule to silently insert std::move just as with return values? Probably because it would be too much burden on the compiler implementors. For example, what if the constructor body was in another translation unit? By contrast, the return value rule simply has to check the symbol tables to determine whether or not the identifier after the return keyword denotes an automatic object.

You can also pass the parameter by value. For move-only types like unique_ptr, it seems there is no established idiom yet. Personally, I prefer to pass by value, as it causes less clutter in the interface.

Special member functions

C++98 implicitly declares three special member functions on demand, that is, when they are needed somewhere: the copy constructor, the copy assignment operator, and the destructor.

X::X(const X&);              // copy constructor
X& X::operator=(const X&);   // copy assignment operator
X::~X();                     // destructor

Rvalue references went through several versions. Since version 3.0, C++11 declares two additional special member functions on demand: the move constructor and the move assignment operator. Note that neither VC10 nor VC11 conforms to version 3.0 yet, so you will have to implement them yourself.

X::X(X&&);                   // move constructor
X& X::operator=(X&&);        // move assignment operator

These two new special member functions are only implicitly declared if none of the special member functions are declared manually. Also, if you declare your own move constructor or move assignment operator, neither the copy constructor nor the copy assignment operator will be declared implicitly.

What do these rules mean in practice?

If you write a class without unmanaged resources, there is no need to declare any of the five special member functions yourself, and you will get correct copy semantics and move semantics for free. Otherwise, you will have to implement the special member functions yourself. Of course, if your class does not benefit from move semantics, there is no need to implement the special move operations.

Note that the copy assignment operator and the move assignment operator can be fused into a single, unified assignment operator, taking its argument by value:

X& X::operator=(X source)    // unified assignment operator
{
    swap(source);            // see my first answer for an explanation
    return *this;
}

This way, the number of special member functions to implement drops from five to four. There is a tradeoff between exception-safety and efficiency here, but I am not an expert on this issue.

Forwarding references (previously known as Universal references)

Consider the following function template:

template<typename T>
void foo(T&&);

You might expect T&& to only bind to rvalues, because at first glance, it looks like an rvalue reference. As it turns out though, T&& also binds to lvalues:

foo(make_triangle());   // T is unique_ptr<Shape>, T&& is unique_ptr<Shape>&&
unique_ptr<Shape> a(new Triangle);
foo(a);                 // T is unique_ptr<Shape>&, T&& is unique_ptr<Shape>&

If the argument is an rvalue of type X, T is deduced to be X, hence T&& means X&&. This is what anyone would expect. But if the argument is an lvalue of type X, due to a special rule, T is deduced to be X&, hence T&& would mean something like X& &&. But since C++ still has no notion of references to references, the type X& && is collapsed into X&. This may sound confusing and useless at first, but reference collapsing is essential for perfect forwarding (which will not be discussed here).

T&& is not an rvalue reference, but a forwarding reference. It also binds to lvalues, in which case T and T&& are both lvalue references.

If you want to constrain a function template to rvalues, you can combine SFINAE with type traits:

#include <type_traits>

template<typename T>
typename std::enable_if<std::is_rvalue_reference<T&&>::value, void>::type
foo(T&&);

Implementation of move

Now that you understand reference collapsing, here is how std::move is implemented:

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

As you can see, move accepts any kind of parameter thanks to the forwarding reference T&&, and it returns an rvalue reference. The std::remove_reference<T>::type meta-function call is necessary because otherwise, for lvalues of type X, the return type would be X& &&, which would collapse into X&. Since t is always an lvalue (remember that a named rvalue reference is an lvalue), but we want to bind t to an rvalue reference, we have to explicitly cast t to the correct return type. The call of a function that returns an rvalue reference is itself an xvalue. Now you know where xvalues come from ;)

The call of a function that returns an rvalue reference, such as std::move, is an xvalue.

Note that returning by rvalue reference is fine in this example, because t does not denote an automatic object, but instead an object that was passed in by the caller.

Tena answered 18/7, 2012 at 11:24 Comment(43)
There's a third reason move semantics are important: exception safety. Often where a copy operation may throw (because it needs to allocate resources and the allocation may fail) a move operation can be no-throw (because it can transfer ownership of existing resources instead of allocating new ones). Having operations that can't fail is always nice, and it can be crucial when writing code that provides exception guarantees.Rossiya
@Rossiya If a member is not dynamic, it should be copied within the move constructor, and that copy could throw an exception. For ensuring exception safety, every member should be forced to be moved with std::move.Ummersen
I was with you right up to 'Universal references', but then it's all too abstract to follow. Reference collapsing? Perfect forwarding? Are you saying that an rvalue reference becomes a universal reference if the type is templated? I wish there was a way to explain this so that I would know if I need to understand it or not! :)Outbound
@Outbound yes. he is just opening the doors to what other concepts rvalue reference also leads to, but since they don't relate fully to move semantics he is brief about those concepts. It's not a problem, just google "universal references" and later perfect forwarding will be pulled from the material you get from that first search, naturally.Long
Please write a book now ... this answer has given me reason to believe if you covered other corners of C++ in a lucid manner like this, thousands of more people will understand it.Musette
@Musette Thank you very much for your kind feedback, I really appreciate it. The problem with writing a book is: it's much more work than you can possibly imagine. If you want to dig deep into C++11 and beyond, I suggest you buy "Effective Modern C++" by Scott Meyers.Tena
Hey fred, guess what? the l/r value system is being reworded/reworked in C++17, and there are some new move mechanics involving destruction of the source.Crowboot
Learning this language is like picking which things you can learn to live without knowing. I can't know EVERYTHING.Encrinite
Is "hello world" a rvalue or an lvalue (in section on Implicit conversion)?Footnote
@Footnote The string itself is an rvalue, you can't take its address.Labialized
@Tena "hello world" that is sent to some_function() is a rvalue. and thanks for your great answer.Labialized
@Labialized - as I thought. Hopefully someone can edit the answer, seems to be a typo :(Footnote
@Footnote Under expr.prim.general, the C++ standard clearly states that A string literal is an lvalue; all other literals are prvalues.Tena
@Labialized Sure you can take its address, const char (*p)[12] = &"hello world"; is perfectly valid C++.Tena
Maybe it's worth to add annotation that std::move will modify it's argumentErland
@liuhao Not sure what you mean by "modify"... The statement std::move(some_variable); does not have any side effects.Tena
uh... I max up the move constractor and std::moveErland
"Being restricted to read from rvalues is not very useful." isnt it just as useful as being restricted to read from lvalues? It just doesnt add anything useful. There is no reason to take a parameter as const T&& because then one could as well take the parameter as const T&, or do I miss something?Fructificative
I read all of this, and it makes a lot of sense, but I think the usage of smart pointers adds too much "inside baseball" that makes the answer inaccessible to the majority who would benefit most. In other words... Congratulations, you created a Wikipedia article relevant to people who already understand most of the topic.Worth
Excellent reference (pun intended). I notice you explain in an elegant way why with move semantics it's fine to simply have a function prototype return by value, especially with the copy and swap idiom. However, you don't quite say (it's trivial) why it's useless to write both prototypes void f(const A&) and void f(A&& a). Moreover, it would be nice to add a small snippet showing how elegantly write a function void f(A&) and an overload void f(A&&) without code duplication, since std::move is going to cost a static_cast.Tenantry
@R.Absil Overloads for const A& and A&& can make sense in some performance-critical scenarios to avoid a cheap (but not zero-cost) move construction. The static_cast inside std::move costs nothing by itself, it generates zero code.Tena
1. ok. 2. You mean "dont complicate stuff, just call f(std::move(a))" ?Tenantry
Why does char d[64]; not benefit from move semantics? Does it have to be allocated with new to be moveable? The comment says "// moving a char array means copying a char array", but why can't you copy a pointer to the array instead?Handedness
@Åsmund Where would you store the pointer? There is no pointer in class cannot_benefit_from_move_semantics. The data member char d[64]; is an array.Tena
@Åsmund @Tena I thought the same and then realise we were probably confusing stack and heap allocated arrays. char d[] is indeed exactly the same as char* d. You can then do d = new char[64]. You could easily move the data of temporary by doing std::swap(d, temporary). But you can't do that with a stack allocated array char d[64]; d=temp; would get you an error. This is because dis just a constant from the compiler point of view. And this makes sense because how would you guarantee that the size of the array would remain the same if you could change the adress of the array?Flyover
@JonasDaverio Inside a struct, char d[] is a flexible array member, which is a C99 feature never adopted into standard C++. Flexible array members are not pointers! The only place where char* d and char d[] (and char d[64]!) are exactly the same is inside function parameter lists, because the compiler adjusts array parameters to pointer parameters.Tena
@Tena That's interesting. You said they were never adopted, so what is the actual situation of char d[] in C++ inside struct? What are the differences with actual pointer and the caveats of thinking them as pointer in general?Flyover
@JonasDaverio I suggest reading stackoverflow.com/questions/3047530 Standards-compliant C++ code simply can't use flexible array members, g++ -pedantic says: ISO C++ forbids flexible array member. A char d[] member is not a pointer in the same sense that a char d[64] member is not a pointer: d stores the array data directly, not a pointer to somewhere else.Tena
@Tena thank you for helping. I think I've understood now what it means, but actually I don't get why an array like char d[64] could not benefit from moving. I know C++ there isn't any pointer, but the compiler internally represent variables by their address, so couldn't you just steal the place we're you put the array when moving an object?Flyover
@JonasDaverio What do you propose we write inside the move assignment operator of a struct X { char d[64]; };? The source and the destination both consist of 64 chars. There are no pointers involved. Our only choice is to copy the 64 chars, because what else could we possibly do instead? At runtime, struct X { char d[64]; }; is laid out exactly like struct Y { char d0; char d1; char d2; ... char d63; } Does that make it more obvious?Tena
@Tena actually, I have trouble too, to understand why you couldn't move chars. That it wouldn't be profitable is understandable since the address of a char takes more space than a char, but with an array, couldn't the compiler do the same as what it does when it moves other object? After all, you don't tell anything more to the compiler than "That things is an xvalue, and you can move it if you want".Flyover
@JonasDaverio Given X a; and X b; what could a = std::move(b); possibly mean besides copying 64 characters? You can't dynamically change the "static address" of a variable if that's what you were implying. &a == &a.d and &b == &b.d will always hold. You can only move "indirect data" (pointed-to array) efficiently, not "direct data" (embedded array).Tena
Oh, I see. So are return value and function arguments all "indirect data"? Why is "direct data" not movable? My understanding, comparing to x86 assembly, is that "direct data" is simply stored in .data section, and "indirect data" is stored on the stack or on the heap. There probably could be a generalization in term of how it works on other platform, but I don't know them. What is different with variables in the .data section and variables on the heap or on the stack?Flyover
@JonasDaverio An example of indirect data would be struct Y { char* p; }; where p is allocated from the heap. Then a shallow Y move (a.p = b.p; b.p = nullptr;) is faster than a deep Y copy (memcpy(a.p, b.p, 64);). The problem with struct X { char d[64]; }; is that it's completely flat; "shallow" vs. "deep" simply does not apply, so memcpy(&a.d, &b.d, 64); is the only possible implementation (for both copy and move).Tena
@JonasDaverio Note that object layout has nothing to do with return values, function arguments or .data/stack/heap. Is an object completely self-contained/flat/shallow in memory? Then move cannot be faster than copy. Does an object hold an external resource via pointer, handle or similar? Then move can be faster than copy. It feels like I have run out of arguments and am just repeating myself. At this point I strongly suggest you implement move semantics on your own simple container type (like std::string or std::vector). Fully grokking move semantics by just reading prose is impossible.Tena
@Tena Keep in mind I'm not trying to prove you wrong, I'm just trying to understand. I know understand that a move constructor is completely useless if you only have flat object as you describe. What I am trying to understand is from a machine point of view. If in C++ you write char a[64] = "blabla..."; char b[64] = a;. Let say you can *guarantee" that you can throw away a, aka you know it is an xvalue. What's preventing the compiler of just stealing the place where you put a to store the data of b?Flyover
@Tena To make my point shorter, quoting you "You can't dynamically change the "static address" of a variable if that's what you were implying." That's exactly the thing I'm implying. I know now it's impossible in C++, by I don't understand why it isn't implemented, and by comparison, why it wouldn't be possible machine-wise.Flyover
@JonasDaverio Reusing the addresses of variables is not what move semantics is about. I wouldn't be surprised if C++ compilers supported such optimizations, but I am neither a C++ compiler writer nor on the standardization committee.Tena
@Tena OK, everything's clear now. Thank you very much for your time helping me.Flyover
@Tena I am reading and re-reading your answer for some references around almost 3 to 4 weeks. Always getting new insights. your answer and work is awesome. Thanks from bottom of heart.Waits
@Tena A a1=make_A(); this did not call Move constructor but implemented fine. If I commented out definition of Move constructor then it gives error and A a1=std::move(make_A()); it actually calls Move constructor. you mentioned std::move just regarding lvalues to convert into rvalues precisely xvalueWaits
@AbhishekMane Any C++ compiler worth its salt will optimize trivial moves/copies such as this away. If you want to disable this optimization for educational purposes, compile with the flag -fno-elide-constructorsTena
@Musette There is now C++ Move Semantics - The Complete Guide (by Nicolai M. Josuttis; completed in 2020) as an exhaustive book on this topic. See cppmove.comPhipps
A
70

Suppose you have a function that returns a substantial object:

Matrix multiply(const Matrix &a, const Matrix &b);

When you write code like this:

Matrix r = multiply(a, b);

then an ordinary C++ compiler will create a temporary object for the result of multiply(), call the copy constructor to initialise r, and then destruct the temporary return value. Move semantics in C++0x allow the "move constructor" to be called to initialise r by copying its contents, and then discard the temporary value without having to destruct it.

This is especially important if (like perhaps the Matrix example above), the object being copied allocates extra memory on the heap to store its internal representation. A copy constructor would have to either make a full copy of the internal representation, or use reference counting and copy-on-write semantics interally. A move constructor would leave the heap memory alone and just copy the pointer inside the Matrix object.

Adamek answered 23/6, 2010 at 22:53 Comment(9)
How are move constructors and copy constructors different?Breban
@dicroce: They differ by syntax, one looks like Matrix(const Matrix& src) (copy constructor) and the other looks like Matrix(Matrix&& src) (move constructor), check my main answer for a better example.Document
@dicroce: One makes a blank object, and one makes a copy. If the data stored in the object is large, a copy can be expensive. For example, std::vector.Asia
Can you please explain what you meant by "discard the temporary value without having to destruct it." Does the move semantics still create a temporary object or does it directly instantitate the return value in the R's address?Lindahl
@kunj2aan: It depends on your compiler, I suspect. The compiler could create a temporary object inside the function, and then move it into the caller's return value. Or, it may be able to directly construct the object in the return value, without needing to use a move constructor.Adamek
Note that the object has to store the resource via a remote handle, like a pointer into the heap for this Matrix class. If the class contains its data directly, then it's up to the element-level move operations to save time (or no savings if the elements are built-ins).Laney
Note a simple test with msvc11 shows the compiler could directly construct the object in the return value, without needing to use a move constructor in release mode.Chromatolysis
@Jichao: That is an optimisation called RVO, see this question for more information on the difference: stackoverflow.com/questions/5031778/…Adamek
@GregHewgill A a1=make_A(); this did not call Move constructor but implemented fine. If I commented out definition of Move constructor then it gives error and A a1=std::move(make_A()); it actually calls Move constructor. fredoverflow mentioned std::move just regarding lvalues to convert into rvalues precisely xvaluesWaits
T
36

Move semantics is about transferring resources rather than copying them when nobody needs the source value anymore.

In C++03, objects are often copied, only to be destroyed or assigned-over before any code uses the value again. For example, when you return by value from a function—unless RVO kicks in—the value you're returning is copied to the caller's stack frame, and then it goes out of scope and is destroyed. This is just one of many examples: see pass-by-value when the source object is a temporary, algorithms like sort that just rearrange items, reallocation in vector when its capacity() is exceeded, etc.

When such copy/destroy pairs are expensive, it's typically because the object owns some heavyweight resource. For example, vector<string> may own a dynamically-allocated memory block containing an array of string objects, each with its own dynamic memory. Copying such an object is costly: you have to allocate new memory for each dynamically-allocated blocks in the source, and copy all the values across. Then you need deallocate all that memory you just copied. However, moving a large vector<string> means just copying a few pointers (that refer to the dynamic memory block) to the destination and zeroing them out in the source.

Trickery answered 8/4, 2012 at 19:47 Comment(0)
U
31

In easy (practical) terms:

Copying an object means copying its "static" members and calling the new operator for its dynamic objects. Right?

class A
{
   int i, *p;

public:
   A(const A& a) : i(a.i), p(new int(*a.p)) {}
   ~A() { delete p; }
};

However, to move an object (I repeat, in a practical point of view) implies only to copy the pointers of dynamic objects, and not to create new ones.

But, is that not dangerous? Of course, you could destruct a dynamic object twice (segmentation fault). So, to avoid that, you should "invalidate" the source pointers to avoid destructing them twice:

class A
{
   int i, *p;

public:
   // Movement of an object inside a copy constructor.
   A(const A& a) : i(a.i), p(a.p)
   {
     a.p = nullptr; // pointer invalidated.
   }

   ~A() { delete p; }
   // Deleting NULL, 0 or nullptr (address 0x0) is safe. 
};

Ok, but if I move an object, the source object becomes useless, no? Of course, but in certain situations that's very useful. The most evident one is when I call a function with an anonymous object (temporal, rvalue object, ..., you can call it with different names):

void heavyFunction(HeavyType());

In that situation, an anonymous object is created, next copied to the function parameter, and afterwards deleted. So, here it is better to move the object, because you don't need the anonymous object and you can save time and memory.

This leads to the concept of an "rvalue" reference. They exist in C++11 only to detect if the received object is anonymous or not. I think you do already know that an "lvalue" is an assignable entity (the left part of the = operator), so you need a named reference to an object to be capable to act as an lvalue. A rvalue is exactly the opposite, an object with no named references. Because of that, anonymous object and rvalue are synonyms. So:

class A
{
   int i, *p;

public:
   // Copy
   A(const A& a) : i(a.i), p(new int(*a.p)) {}

   // Movement (&& means "rvalue reference to")
   A(A&& a) : i(a.i), p(a.p)
   {
      a.p = nullptr;
   }

   ~A() { delete p; }
};

In this case, when an object of type A should be "copied", the compiler creates a lvalue reference or a rvalue reference according to if the passed object is named or not. When not, your move-constructor is called and you know the object is temporal and you can move its dynamic objects instead of copying them, saving space and memory.

It is important to remember that "static" objects are always copied. There's no ways to "move" a static object (object in stack and not on heap). So, the distinction "move"/ "copy" when an object has no dynamic members (directly or indirectly) is irrelevant.

If your object is complex and the destructor has other secondary effects, like calling to a library's function, calling to other global functions or whatever it is, perhaps is better to signal a movement with a flag:

class Heavy
{
   bool b_moved;
   // staff

public:
   A(const A& a) { /* definition */ }
   A(A&& a) : // initialization list
   {
      a.b_moved = true;
   }

   ~A() { if (!b_moved) /* destruct object */ }
};

So, your code is shorter (you don't need to do a nullptr assignment for each dynamic member) and more general.

Other typical question: what is the difference between A&& and const A&&? Of course, in the first case, you can modify the object and in the second not, but, practical meaning? In the second case, you can't modify it, so you have no ways to invalidate the object (except with a mutable flag or something like that), and there is no practical difference to a copy constructor.

And what is perfect forwarding? It is important to know that a "rvalue reference" is a reference to a named object in the "caller's scope". But in the actual scope, a rvalue reference is a name to an object, so, it acts as a named object. If you pass an rvalue reference to another function, you are passing a named object, so, the object isn't received like a temporal object.

void some_function(A&& a)
{
   other_function(a);
}

The object a would be copied to the actual parameter of other_function. If you want the object a continues being treated as a temporary object, you should use the std::move function:

other_function(std::move(a));

With this line, std::move will cast a to an rvalue and other_function will receive the object as a unnamed object. Of course, if other_function has not specific overloading to work with unnamed objects, this distinction is not important.

Is that perfect forwarding? Not, but we are very close. Perfect forwarding is only useful to work with templates, with the purpose to say: if I need to pass an object to another function, I need that if I receive a named object, the object is passed as a named object, and when not, I want to pass it like a unnamed object:

template<typename T>
void some_function(T&& a)
{
   other_function(std::forward<T>(a));
}

That's the signature of a prototypical function that uses perfect forwarding, implemented in C++11 by means of std::forward. This function exploits some rules of template instantiation:

 `A& && == A&`
 `A&& && == A&&`

So, if T is a lvalue reference to A (T = A&), a also (A& && => A&). If T is a rvalue reference to A, a also (A&& && => A&&). In both cases, a is a named object in the actual scope, but T contains the information of its "reference type" from the caller scope's point of view. This information (T) is passed as template parameter to forward and 'a' is moved or not according to the type of T.

Ummersen answered 18/8, 2013 at 15:57 Comment(0)
B
30

If you are really interested in a good, in-depth explanation of move semantics, I'd highly recommend reading the original paper on them, "A Proposal to Add Move Semantics Support to the C++ Language."

It's very accessible and easy to read and it makes an excellent case for the benefits that they offer. There are other more recent and up to date papers about move semantics available on the WG21 website, but this one is probably the most straightforward since it approaches things from a top-level view and doesn't get very much into the gritty language details.

Birnbaum answered 23/6, 2010 at 23:32 Comment(0)
J
27

It's like copy semantics, but instead of having to duplicate all of the data you get to steal the data from the object being "moved" from.

Jerz answered 23/6, 2010 at 22:56 Comment(0)
D
15

You know what a copy semantics means right? it means you have types which are copyable, for user-defined types you define this either buy explicitly writing a copy constructor & assignment operator or the compiler generates them implicitly. This will do a copy.

Move semantics is basically a user-defined type with constructor that takes an r-value reference (new type of reference using && (yes two ampersands)) which is non-const, this is called a move constructor, same goes for assignment operator. So what does a move constructor do, well instead of copying memory from it's source argument it 'moves' memory from the source to the destination.

When would you want to do that? well std::vector is an example, say you created a temporary std::vector and you return it from a function say:

std::vector<foo> get_foos();

You're going to have overhead from the copy constructor when the function returns, if (and it will in C++0x) std::vector has a move constructor instead of copying it can just set it's pointers and 'move' dynamically allocated memory to the new instance. It's kind of like transfer-of-ownership semantics with std::auto_ptr.

Document answered 23/6, 2010 at 22:58 Comment(1)
I don't think this is a great example, because in these function return value examples the Return Value Optimization is probably already eliminating the copy operation.Raseta
G
9

I'm writing this to make sure I understand it properly.

Move semantics were created to avoid the unnecessary copying of large objects. Bjarne Stroustrup in his book "The C++ Programming Language" uses two examples where unnecessary copying occurs by default: one, the swapping of two large objects, and two, the returning of a large object from a method.

Swapping two large objects usually involves copying the first object to a temporary object, copying the second object to the first object, and copying the temporary object to the second object. For a built-in type, this is very fast, but for large objects these three copies could take a large amount of time. A "move assignment" allows the programmer to override the default copy behavior and instead swap references to the objects, which means that there is no copying at all and the swap operation is much faster. The move assignment can be invoked by calling the std::move() method.

Returning an object from a method by default involves making a copy of the local object and its associated data in a location which is accessible to the caller (because the local object is not accessible to the caller and disappears when the method finishes). When a built-in type is being returned, this operation is very fast, but if a large object is being returned, this could take a long time. The move constructor allows the programmer to override this default behavior and instead "reuse" the heap data associated with the local object by pointing the object being returned to the caller to heap data associated with the local object. Thus no copying is required.

In languages which do not allow the creation of local objects (that is, objects on the stack) these types of problems do not occur as all objects are allocated on the heap and are always accessed by reference.

Groggery answered 18/11, 2016 at 23:12 Comment(2)
"A "move assignment" allows the programmer to override the default copy behavior and instead swap references to the objects, which means that there is no copying at all and the swap operation is much faster." - these claims are ambiguous and misleading. To swap two objects x and y, you can't just "swap references to the objects"; it may be that the objects contain pointers that reference other data, and those pointers can be swapped, but move operators are not required to swap anything. They may wipe out the data from the moved-from object, rather than preserve the dest data therein.Nadaba
You could write swap() without move semantics. "The move assignment can be invoked by calling the std::move() method." - it's sometimes necessary to use std::move() - though that doesn't actually move anything - just lets the compiler know the argument is movable, sometimes std::forward<>() (with forwarding references), and other times the compiler knows a value can be moved.Nadaba
T
8

To illustrate the need for move semantics, let's consider this example without move semantics:

Here's a function that takes an object of type T and returns an object of the same type T:

T f(T o) { return o; }
  //^^^ new object constructed

The above function uses call by value which means that when this function is called an object must be constructed to be used by the function.
Because the function also returns by value, another new object is constructed for the return value:

T b = f(a);
  //^ new object constructed

Two new objects have been constructed, one of which is a temporary object that's only used for the duration of the function.

When the new object is created from the return value, the copy constructor is called to copy the contents of the temporary object to the new object b. After the function completes, the temporary object used in the function goes out of scope and is destroyed.


Now, let's consider what a copy constructor does.

It must first initialize the object, then copy all the relevant data from the old object to the new one.
Depending on the class, maybe its a container with very much data, then that could represent much time and memory usage

// Copy constructor
T::T(T &old) {
    copy_data(m_a, old.m_a);
    copy_data(m_b, old.m_b);
    copy_data(m_c, old.m_c);
}

With move semantics it's now possible to make most of this work less unpleasant by simply moving the data rather than copying.

// Move constructor
T::T(T &&old) noexcept {
    m_a = std::move(old.m_a);
    m_b = std::move(old.m_b);
    m_c = std::move(old.m_c);
}

Moving the data involves re-associating the data with the new object. And no copy takes place at all.

This is accomplished with an rvalue reference.
An rvalue reference works pretty much like an lvalue reference with one important difference:
an rvalue reference can be moved and an lvalue cannot.

From cppreference.com:

To make strong exception guarantee possible, user-defined move constructors should not throw exceptions. In fact, standard containers typically rely on std::move_if_noexcept to choose between move and copy when container elements need to be relocated. If both copy and move constructors are provided, overload resolution selects the move constructor if the argument is an rvalue (either a prvalue such as a nameless temporary or an xvalue such as the result of std::move), and selects the copy constructor if the argument is an lvalue (named object or a function/operator returning lvalue reference). If only the copy constructor is provided, all argument categories select it (as long as it takes a reference to const, since rvalues can bind to const references), which makes copying the fallback for moving, when moving is unavailable. In many situations, move constructors are optimized out even if they would produce observable side-effects, see copy elision. A constructor is called a 'move constructor' when it takes an rvalue reference as a parameter. It is not obligated to move anything, the class is not required to have a resource to be moved and a 'move constructor' may not be able to move a resource as in the allowable (but maybe not sensible) case where the parameter is a const rvalue reference (const T&&).

Tlingit answered 25/2, 2016 at 0:0 Comment(0)
C
-1

Move semantics is a feature in programming languages that allows for the efficient transfer of data from one object to another without unnecessary copying or allocation of memory. It optimizes the performance of programs by allowing developers to move the resources (such as memory) of one object into another object, instead of creating a copy. This is particularly useful for resource-intensive operations, such as handling large data sets or managing memory in resource-constrained environments. Move semantics is supported by most object-oriented programming languages, such as C++, Java, and Python.

Cathe answered 2/3 at 10:49 Comment(2)
tell me more about move semantics in python and javaLocket
What is your language model called?Gustation
C
-2

Here's an answer from the book "The C++ Programming Language" by Bjarne Stroustrup. If you don't want to see the video, you can see the text below:

Consider this snippet. Returning from an operator+ involves copying the result out of the local variable res and into someplace where the caller can access it.

Vector operator+(const Vector& a, const Vector& b)
{
    if (a.size()!=b.size())
        throw Vector_siz e_mismatch{};
    Vector res(a.size());
        for (int i=0; i!=a.size(); ++i)
            res[i]=a[i]+b[i];
    return res;
}

We didn’t really want a copy; we just wanted to get the result out of a function. So we need to move a Vector rather than to copy it. We can define move constructor as follows:

class Vector {
    // ...
    Vector(const Vector& a); // copy constructor
    Vector& operator=(const Vector& a); // copy assignment
    Vector(Vector&& a); // move constructor
    Vector& operator=(Vector&& a); // move assignment
};

Vector::Vector(Vector&& a)
    :elem{a.elem}, // "grab the elements" from a
    sz{a.sz}
{
    a.elem = nullptr; // now a has no elements
    a.sz = 0;
}

The && means "rvalue reference" and is a reference to which we can bind an rvalue. "rvalue"’ is intended to complement "lvalue" which roughly means "something that can appear on the left-hand side of an assignment." So an rvalue means roughly "a value that you can’t assign to", such as an integer returned by a function call, and the res local variable in operator+() for Vectors.

Now, the statement return res; will not copy!

Clarke answered 25/4, 2020 at 5:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.