2

If I have a unnamed unique_ptr object, holding a object Foo, why will the object not be deleted when it is added to a vector?

Example:

vec.push_back(std::unique_ptr<Foo>(new Foo())));

Why would Foo not be deleted? I've attempted to implement this but I can't seem to figure this out, as I delete the object in the destructor.

Krystian S
  • 1,586
  • 7
  • 23

3 Answers3

6

The argument to std::vector::push_back is a temporary, so it will get passed to push_back as an rvalue reference. As you can see in the documentation, push_back has two forms, and the second takes an rvalue reference. The unique_ptr will get moved into the vector, and the temporary that you created for the argument will no longer own the memory.

You can't copy an std::unique_ptr; if you tried to, you would get a compile error. However, moving a unique pointer is perfectly allowable; the previous owner no longer owns the memory that it once pointed to.

Silvio Mayolo
  • 62,821
  • 6
  • 74
  • 116
  • I have a class that functions almost identical to a unique_ptr, but after it is moved, the destructor is called, and the memory is released, so the new object in the vector now is pointing to the invalid memory that has been deleted. I set the old pointed to nullptr in the move constructor and this still happens, any ideas? – Krystian S Jul 18 '18 at 03:34
  • 1
    Setting the old pointer to `nullptr` is exactly what `unique_ptr` does internally, so you're making the right move. I suggest posting a new question about your custom-baked class and including the class' code. – Silvio Mayolo Jul 18 '18 at 03:39
  • Thanks for the response! I just figured it out - I should not have a copy constructor as only one object should own the pointer at once. – Krystian S Jul 18 '18 at 03:40
3

In this case, since you're passing a temporary object, the move overload is called: void std::vector<T>::push_back(T&&). Move semantics allow for nice optimization in situations where expensive copies aren't necessary. Without them, push_back would need to make a copy of the unique_ptr (which actually is not possible with the semantics of unique_ptr) and you would indeed see the destructor being called. With move semantics, the new unique_ptr inside the vector is essentially allowed to steal the contents of the temporary, and no copying takes place. See here for more explanation of move semantics.

alter_igel
  • 6,899
  • 3
  • 21
  • 40
2

This happens because unique_ptr doesn't have a copy constructor and instead must be moved everywhere.

Here's what happens, in C++11 or 14, ignoring copy/move elision (because the rules changed, we're not allowed to ignore that in C++17):

  1. You construct a temporary unique_ptr<Foo>. This has the value category of prvalue, a type of rvalue.
  2. The push_back method is called. Because the parameter is the temporary created in 1., the second (rvalue reference) overload is called. This means that the value is moved in to vector.
  3. The vector uses unique_ptr's move constructor (the fifth overload on that page), which creates a unique_ptr<Foo> managing the same Foo that the temporary in 1 does, and makes the temporary hold nullptr instead.
  4. The statement completes and the temporary from 1. needs to be destroyed. Its destructor is called. Because it holds nullptr, this does nothing in particular.
Daniel H
  • 7,223
  • 2
  • 26
  • 41