Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

So can unique_ptr be used safely in stl collections?

I am confused with unique_ptr and rvalue move philosophy.

Let's say we have two collections:

std::vector<std::auto_ptr<int>> autoCollection; std::vector<std::unique_ptr<int>> uniqueCollection; 

Now I would expect the following to fail, as there is no telling what the algorithm is doing internally and maybe making internal pivot copies and the like, thus ripping away ownership from the auto_ptr:

std::sort(autoCollection.begin(), autoCollection.end()); 

I get this. And the compiler rightly disallows this happening.

But then I do this:

std::sort(uniqueCollection.begin(), uniqueCollection.end()); 

And this compiles. And I do not understand why. I did not think unique_ptrs could be copied. Does this mean a pivot value cannot be taken, so the sort is less efficient? Or is this pivot actually a move, which in fact is as dangerous as the collection of auto_ptrs, and should be disallowed by the compiler?

I think I am missing some crucial piece of information, so I eagerly await someone to supply me with the aha! moment.

like image 972
DanDan Avatar asked May 20 '10 18:05

DanDan


People also ask

When should we use unique_ptr?

Use unique_ptr when if you want to have single ownership(Exclusive) of resource. Only one unique_ptr can point to one resource. Since there can be one unique_ptr for single resource its not possible to copy one unique_ptr to another.

Why Auto_ptr Cannot be used with STL?

Why is auto_ptr deprecated? It takes ownership of the pointer in a way that no two pointers should contain the same object. Assignment transfers ownership and resets the rvalue auto pointer to a null pointer. Thus, they can't be used within STL containers due to the aforementioned inability to be copied.

What happens to unique_ptr after move?

"Moving" transfers ownership to a new unique_ptr and resets the old unique_ptr .

What does unique_ptr get do?

unique_ptr::getReturns a pointer to the managed object or nullptr if no object is owned.


2 Answers

I think it's more a question of philosophy than technic :)

The underlying question is what is the difference between Move and Copy. I won't jump into technical / standardista language, let's do it simply:

  • Copy: create another identical object (or at least, one which SHOULD compare equal)
  • Move: take an object and put it in another location

As you said, it is possible to implement Move in term of Copy: create a copy into the new location and discard the original. However there are two issues there. One is of performance, the second is about objects used for RAII: which of the two should have ownership ?

A proper Move constructor solves the 2 issues:

  • It is clear which object has ownership: the new one, since the original will be discarded
  • It is thus unnecessary to copy the resources pointed to, which allows for greater efficiency

The auto_ptr and unique_ptr are a very good illustration of this.

With an auto_ptr you have a screwed Copy semantic: the original and the copy don't compare equal. You could use it for its Move semantic but there is a risk that you'll lose the object pointed to somewhere.

On the other hand, the unique_ptr is exactly that: it guarantees a unique owner of the resource, thus avoiding copying and the inevitable deletion issue that would follow. And the no-copy is guaranteed at compile-time too. Therefore, it's suitable in containers as long as you don't try to have copy initialization.

typedef std::unique_ptr<int> unique_t; typedef std::vector< unique_t > vector_t;  vector_t vec1;                           // fine vector_t vec2(5, unique_t(new Foo));     // Error (Copy) vector_t vec3(vec1.begin(), vec1.end()); // Error (Copy) vector_t vec3(make_move_iterator(vec1.begin()), make_move_iterator(vec1.end()));     // Courtesy of sehe  std::sort(vec1.begin(), vec1.end()); // fine, because using Move Assignment Operator  std::copy(vec1.begin(), vec1.end(), std::back_inserter(vec2)); // Error (copy) 

So you can use unique_ptr in a container (unlike auto_ptr), but a number of operations will be impossible because they involve copying which the type does not support.

Unfortunately Visual Studio may be quite lax in the enforcement of the standard and also has a number of extensions that you would need to disable to ensure portability of the code... don't use it to check the standard :)

like image 69
Matthieu M. Avatar answered Oct 16 '22 15:10

Matthieu M.


The unique_ptrs are being moved using their move constructor. unique_ptr is Movable, but not CopyConstructable.

There's a great article on rvalue references here. If you haven't read about them yet, or are confused, take a look!

like image 45
rlbond Avatar answered Oct 16 '22 16:10

rlbond