A container of unique_ptr
seems to make little sense: you cannot use it with initializer lists and I failed to iterate through the container (workarounds below). Am I misunderstanding something? Or when does it make sense to use unique_ptr
and STL containers?
#include <memory>
#include <vector>
using namespace std;
struct Base { void go() { } virtual ~Base() { } };
// virtual ~Base() = default; gives
// "declared virtual cannot be defaulted in the class body" why?
class Derived : public Base { };
int main() {
//vector<unique_ptr<Base>> v1 = { new Derived, new Derived, new Derived };
//vector<shared_ptr<Base>> v2 = { new Derived, new Derived, new Derived };
vector<Base*> v3 = { new Derived, new Derived, new Derived };
vector<shared_ptr<Base>> v4(v3.begin(), v3.end());
vector<unique_ptr<Base>> v5(v3.begin(), v3.end());
for (auto i : v5) { // works with v4
i->go();
}
return 0;
}
How to initialize a container of noncopyable with initializer list?
Can I list-initialize a vector of move-only type?
when I need containers of NoCopy types I usually use boost::ptr_vector
or std::vector<shared_ptr>
Use unique_ptr when you want to have single ownership(Exclusive) of the 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. A shared_ptr is a container for raw pointers.
The C++ Standard says that an STL element must be "copy-constructible" and "assignable." In other words, an element must be able to be assigned or copied and the two elements are logically independent. std::auto_ptr does not fulfill this requirement.
std::unique_ptr is a smart pointer that owns and manages another object through a pointer and disposes of that object when the unique_ptr goes out of scope. The object is disposed of, using the associated deleter when either of the following happens: the managing unique_ptr object is destroyed.
for (auto i : v5) {
i->go();
}
Should be
for (auto& i : v5) { // note 'auto&'
i->go();
}
Else you'll try to copy the current element.
Also, you can't use an initializer list like that, because the constructors of std::unique_ptr
and std::shared_ptr
are marked explicit
. You need to do something like this:
#include <iterator> // make_move_iterator, begin, end
template<class T>
std::unique_ptr<T> make_unique(){ // naive implementation
return std::unique_ptr<T>(new T());
}
std::unique_ptr<Base> v1_init_arr[] = {
make_unique<Derived>(), make_unique<Derived>(), make_unique<Derived>()
};
// these two are only for clarity
auto first = std::make_move_iterator(std::begin(v1_init_arr));
auto last = std::make_move_iterator(std::end(v1_init_arr));
std::vector<std::unique_ptr<Base>> v1(first, last);
std::vector<std::shared_ptr<Base>> v2 = {
std::make_shared<Derived>(),
std::make_shared<Derived>(),
std::make_shared<Derived>()
};
And this is a Good Thing™, because otherwise you might leak memory (if one of the later constructors throws, the former ones aren't yet bound to the smart pointers). The tip-toeing for the unique_ptr
is necessary, because initializer lists copy their arguments, and since unique_ptr
s aren't copyable, you'd get a problem.
That said, I use a std::map<std::string, std::unique_ptr<LoaderBase>>
for a dictionary of loaders in one of my projects.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With