Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do we need to set rvalue reference to null in move constructor?

//code from https://skillsmatter.com/skillscasts/2188-move-semanticsperfect-forwarding-and-rvalue-references
class Widget {
public:
    Widget(Widget&& rhs)
        : pds(rhs.pds) // take source’s value
    { 
        rhs.pds = nullptr;  // why??
    }

private:
    struct DataStructure;
    DataStructure *pds;
};

I can't understand the reason for setting rhd.pds to nullptr .

What will happen if we remove this line : rhs.pds = nullptr;

like image 904
uchar Avatar asked Mar 01 '14 11:03

uchar


People also ask

Why do we need rvalue reference?

Rvalue references allow programmers to avoid logically unnecessary copying and to provide perfect forwarding functions. They are primarily meant to aid in the design of higer performance and more robust libraries.

Why do we need move constructor?

A move constructor enables the resources owned by an rvalue object to be moved into an lvalue without copying.

What is R value Referene in C ++ 11?

An rvalue reference is a reference that will bind only to a temporary object. What do I mean? Prior to C++11, if you had a temporary object, you could use a "regular" or "lvalue reference" to bind it, but only if it was const: 1. 2.

Why is std :: move necessary?

std::move is used to indicate that an object t may be "moved from", i.e. allowing the efficient transfer of resources from t to another object. In particular, std::move produces an xvalue expression that identifies its argument t . It is exactly equivalent to a static_cast to an rvalue reference type.


1 Answers

Some details of the class have been removed. In particular, the constructor dynamically allocates the DataStructure object and the destructor deallocates it. If, during a move, you just copied the pointer from one Widget to another, both Widgets would have pointers to the same allocated DataStructure object. Then, when those objects are destroyed, they would both attempt to delete it. This would give undefined behaviour. To avoid this, the Widget that is being moved from has its internal pointer to set to nullptr.

This a standard pattern when implementing a move constructor. You want to move ownership of some dynamically allocated objects from one object to another, so you need to make sure the original object no longer owns those allocated objects.

Diagrammatically, you start off with this situation, wanting to move ownership of the DataStructure from one Widget to the other:

    ┌────────┐        ┌────────┐
    │ Widget │        │ Widget │
    └───╂────┘        └────────┘
        ┃
        ▼
 ┌───────────────┐
 │ DataStructure │
 └───────────────┘

If you just copied the pointer, you'd have:

    ┌────────┐        ┌────────┐
    │ Widget │        │ Widget │
    └───╂────┘        └───╂────┘
        ┗━━━━━━━━┳━━━━━━━┛
                  ▼
         ┌───────────────┐
         │ DataStructure │
         └───────────────┘

If you then set the original Widget pointer to nullptr, you have:

    ┌────────┐         ┌────────┐
    │ Widget │         │ Widget │
    └────────┘         └───╂────┘
                           ┃
                           ▼
                  ┌───────────────┐
                  │ DataStructure │
                  └───────────────┘

Ownership has successfully been transferred, and when both Widgets can be destroyed without causing undefined behaviour.

like image 62
Joseph Mansfield Avatar answered Oct 26 '22 11:10

Joseph Mansfield