I'm trying to follow Herb Sutter's C++ guidelines, in this case to prefer unique_ptr
to raw pointers and shared_ptr
. One of the arguments in favour of std::unique_ptr
is convertibility to shared_ptr
should that be needed at some point.
In my case I have a vector
of unique_ptr
that I need to pass to a method that takes a vector
of shared_ptr
. I was hoping to be able to write something like:
for (auto &uniquePtr : vectorUnique)
vectorShared.push_back(make_shared<Abstract>(move(uniquePtr));
This gives me the following error with my Xcode 7.1
based toolchain configured for C++11
:
error: field type 'Abstract' is an abstract class.
It seems the STL is trying to hold a concrete instance of type Abstract
when I use make_shared
. That would seem to make Mr. Sutter's advice unworkable in many cases, so I'm sure I must be doing something wrong! I've resorted to writing:
for (auto &uniquePtr : vectorUnique) {
auto ptr = uniquePtr.get();
auto shared = shared_ptr<Abstract>(ptr);
vectorShared.push_back(shared);
uniquePtr.release();
}
Is there a better way to do this?
make_shared
constructs a new object using the given arguments and returns a shared_ptr
to it. So the compiler expects a constructor Abstract(std::unique_ptr<Abstract>)
, which is probably not what you have.
What you want is the constructor of shared_ptr
that takes a unique_ptr
argument:
vectorShared.push_back(shared_ptr<Abstract>(move(uniquePtr)));
and, as it's not explicit
, then
vectorShared.emplace_back(move(uniquePtr));
will just work (I've used emplace_back
to avoid redundant copying, at Richard Hodges's suggestion). There's even a standard algorithm, so you don't need the for
loop:
std::move(vectorUnique.begin(), vectorUnique.end(),
std::back_inserter(vectorShared));
If you need this regularly, you might define a function:
#include <vector>
#include <memory>
#include <algorithm>
template<typename T>
std::vector<std::shared_ptr<T>>
convert_to_shared(std::vector<std::unique_ptr<T>>&& vu)
{
using std::begin;
using std::end;
std::vector<std::shared_ptr<T>> vs;
vs.reserve(vu.size());
std::move(begin(vu), end(vu), std::back_inserter(vs));
return vs;
}
// Example of use
class Abstract {};
int main()
{
std::vector<std::unique_ptr<Abstract>> vectorUnique;
std::vector<std::shared_ptr<Abstract>> vectorShared
= convert_to_shared(std::move(vectorUnique));
}
Sorry about the terrible name (I'm open to suggestions). If you omit the call to reserve()
, you could generalise it to more containers.