Using std::unique_ptr here is simply wasteful unless your target is a compiler firewall (basically hides the compilation time dependency with the vector, but then you need a forward declaration for standard containers).
You add indirection, but, more importantly, the full content of SomeClass turns into 3 separate memory blocks for loading when accessing the content ( SomeClass combined with / containing unique_ptr's block pointing to std::vector's block pointing to its array of elements). In addition, you pay one extra extra level of heap overhead.
Now you can come up with scenarios in which indirect handling is useful for vector , for example, perhaps you can finely move / swap unique_ptrs between two instances of SomeClass . Yes, but vector already provides that without a unique_ptr wrapper unique_ptr top. And it already has states like empty that you can reuse some validity/nilness .
Remember that variable-sized containers themselves are small objects, not large objects that point to potentially large blocks. vector is small, its dynamic content may be. The idea of โโadding pointers to large objects is not a bad rule of thumb, but vector not a large object. With the semantics of moving in place, you should think of it more as a small block of memory, indicating a large one that can be finely copied and swapped cheaply. Before moving on to semantics, there was more reason to think of something like std::vector as an irreplaceably large object (although its contents were always replaced), but now you should think of it rather as a small descriptor pointing to a large dynamic content.
Some common reasons for introducing indirection through something like unique_ptr :
- Abstraction and hiding. If you are trying to abstract or hide the specific definition of a type / subtype,
Foo , then you need indirectness so that its handle can be captured (or perhaps even used with abstraction) by those who know exactly what Foo . - To allow a large, continuous object with 1 block type to be passed from owner to owner without invoking a copy or nullifying references / pointers (including iterators) to it or its contents.
- It seems that it is wasteful, but sometimes useful in an urgent endeavor - to simply enter a
validity/null state that essentially does not have it. - It is sometimes useful to use optimizations to pull out some of the less frequently visited larger members of an object so that its regular access elements are more neat (and possibly with neighboring objects) in the cache line. There,
unique_ptr can allow you to share this memory layout of the object, still corresponding to RAII.
Now packing shared_ptr on top of a standard container may have more legitimate applications if you have a container that can actually be the owner (reasonably) of more than one owner. With unique_ptr only one owner can own an object at a time, and standard containers already allow you to change and move each other with their internal guts (large dynamic parts). Therefore, there are very few reasons why I can think of wrapping a standard container directly with unique_ptr , since it already looks a bit like a smart pointer to a dynamic array (but with more functionality for working with these dynamic data, including deep copy, if optional).
And if we are talking about non-standard containers, for example, we say that you are working with a third-party library that provides some data structures whose contents can become very large, but they cannot provide these cheap, invalid move/swap semantics, then you can superficially wrap it around unique_ptr , exchanging some of its creation / access / destruction rights to return these cheap move/swap semantics as a workaround. For standard containers, this workaround is not required.