4

First of all, I know that we should use std::make_unique() instead of calling the std::unique_ptr constructor and I know why.

But I was looking at the documentation of std::unique_ptr to pass the time and sharpen my knowledge about it and I found the following examples about the constructor usage:

// unique_ptr constructor example #include <iostream> #include <memory> int main () { std::default_delete<int> d; std::unique_ptr<int> u1; std::unique_ptr<int> u2 (nullptr); std::unique_ptr<int> u3 (new int); std::unique_ptr<int> u4 (new int, d); std::unique_ptr<int> u5 (new int, std::default_delete<int>()); std::unique_ptr<int> u6 (std::move(u5)); std::unique_ptr<int> u7 (std::move(u6)); std::unique_ptr<int> u8 (std::auto_ptr<int>(new int)); std::cout << "u1: " << (u1?"not null":"null") << '\n'; std::cout << "u2: " << (u2?"not null":"null") << '\n'; std::cout << "u3: " << (u3?"not null":"null") << '\n'; std::cout << "u4: " << (u4?"not null":"null") << '\n'; std::cout << "u5: " << (u5?"not null":"null") << '\n'; std::cout << "u6: " << (u6?"not null":"null") << '\n'; std::cout << "u7: " << (u7?"not null":"null") << '\n'; std::cout << "u8: " << (u8?"not null":"null") << '\n'; return 0; } 

It generates (and I verified it by executing the code) the following results:

u1: null
u2: null
u3: not null
u4: not null
u5: null
u6: null
u7: not null
u8: not null

What I'm struggling to understand is:

  • Why is u4 valid while u5 is not (nullptr) ?
  • Why is u7 valid but not u6 ?

Perhaps these questions are very basic but I'm completely missing the point.

If anyone could enlighten me about these questions, I would be grateful.

2
  • 1
    u5 died while initializing u6, and then u6 died while initializing u7. std::unique_ptr will become null when moved from, by design, to enforce its guarantee of unique ownership. Commented Sep 19, 2019 at 14:50
  • 1
    @AlanBirtles TLDR: exception safety when creating multiple unique_ptr variables as function arguments and one of them throws. See e.g. stackoverflow.com/a/22571331/256138 Commented Sep 19, 2019 at 15:00

3 Answers 3

6

std::move is named move for a reason. When you move from one std::unique_ptr to another, the one you move from becomes nullptr. It couldn't be any other way, it's a unique ptr after all, and two unique_ptr instances sharing the same data would violate that. (Also both going out of scope would call the deleter twice, and then all hell is loose.)

Sign up to request clarification or add additional context in comments.

Comments

5

Why is u4 valid while u5 is not (nullptr) ?

Because u5 was moved from in the initialisation of u6, while u4 has not been moved from. The move constructor is guaranteed to set the moved from pointer to null. It cannot point to the same object as u6 since that would violate the uniqueness constraint.

Why is u7 valid but not u6 ?

Same reason.

1 Comment

Oh right, I'm so stupid... tired. Thanks for the explanation.
3

Moving a uniquely owned object means that the new owner has it and the previous owner has nothing left.
Much like how physical objects work in the real world.

Having nothing is indicated by the owner’s converting to a null pointer.

The object that was initially owned by u5 has been moved first into u6 and then into u7.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.