Reputation: 58471
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?
when I need containers of NoCopy types I usually use boost::ptr_vector
or std::vector<shared_ptr>
Upvotes: 7
Views: 5976
Reputation: 131799
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.
Upvotes: 15
Reputation: 63775
unique_ptr
makes sense in STL containers when the container holds objects that can't be copied. Or if it's costly or simply incorrect to copy them.
You get the same functionality as your
vector<Base*> v3 = { new Derived, new Derived, new Derived };
But without the memory leaks that v3
is inviting.
Upvotes: 1
Reputation: 32510
You actually can iterate though the container without issues using std::unique_ptr<T>
... you just either need to access a reference (i.e., not a copy) of the unique pointer, or you need to actually use an iterator-type with the container. In your case that would be something like vector<unique_ptr<Base>>::iterator
or vector<unique_ptr<Base>>::const_iterator
.
Upvotes: 1