How do move semantics work with unique_ptr?

2019-05-10 06:16发布

I was experimenting with using unique_ptr and wrote some simple code to check how it works with move semantics.

#include <iostream>
#include <vector>
using namespace std;

class X
{
public:
    X(){}
    ~X() { cout << "Destructor X" << endl; }
    void Print() { cout << "X" << endl; }
};

int main()
{
    unique_ptr<X> ptr(new X());
    ptr->Print();

    vector<unique_ptr<X>> v;
    v.push_back(move(ptr));
    ptr->Print();
    v.front()->Print();

    return 0;
}

The output is as follows:

X
X
X
Destructor X

My expectation was that the original unique_ptr ptr would be invalidated after the push_back. But the Print() method is called just fine. What would be the explanation for this behavior?

2条回答
爷、活的狠高调
2楼-- · 2019-05-10 06:38

What you have is plain undefined behavior. If I replace the contents of main with the following

int main()
{
    unique_ptr<X> ptr;
    ptr->Print();
    cout << (static_cast<bool>(ptr) ? "active\n" : "inactive\n");
}

Both gcc and clang still print

X
inactive

You're calling a member function on a nullptr, and I'm guessing it just happens to work because the member function doesn't actually make use of the this pointer. Change your class definition to:

class X
{
    int y = 0;
public:
    X(){}
    ~X() { cout << "Destructor X" << endl; }
    void Print() { cout << "y = " << y << endl; }
};

Now your original code should result in a segmentation fault because it'll attempt to dereference nullptr.


As for your expectation that unique_ptr will be invalidated after you move from it, you're absolutely correct. This is guaranteed by the standard.

§20.8.1/4 [unique.ptr]

Additionally, u can, upon request, transfer ownership to another unique pointer u2. Upon completion of such a transfer, the following postconditions hold:
u2.p is equal to the pre-transfer u.p,
u.p is equal to nullptr, and
...

Above u & u2 are unique_ptr objects, and p is the pointer to the managed object.

查看更多
爱情/是我丢掉的垃圾
3楼-- · 2019-05-10 06:42

My expectation was that the original unique_ptr ptr would be invalidated after the push_back.

It's set to a null pointer. You can check that by comparing it to nullptr.

But the Print() method is called just fine. What would be the explanation for this behavior?

You're calling a member function on a null pointer, which is undefined behaviour. That member function doesn't actually access any data in the class, so it doesn't crash, but it's still undefined behaviour.

You get similar behaviour for this program, it has nothing to do with unique_ptr:

int main()
{
  X x;
  X* ptr = &x;
  ptr->Print();
  ptr = nullptr;
  ptr->Print();
}

It appears to work fine because X::Print() doesn't actually read anything from the this pointer. If you change the definition of X::Print() to access some member data in the class you'll probably get a crash due to dereferencing a null pointer.

See When does invoking a member function on a null instance result in undefined behavior? for more information.

查看更多
登录 后发表回答