Somtimes there is a case that we have a factory that produces a vector of std::unique_ptr and later on we would like share these pointers between classes/threads/you name it. So it would be desirable to use std::shared_ptr instead. There is a way of course to convert std::uniqe_ptr to std::shared_ptr
std::shared_ptr<int> sharedV;
std::unique_ptr<int> uniqueV(new int(2));
sharedV = std::move(uniqueV);
So is there any straightforward way to do such thing with std collections?
You can use std::move
from <algorithm>
to move ranges. It behaves a lot like std::copy
, but moves instead. The following example will move all unique_ptr
from uniqueV
into sharedV
. uniqueV
's elements will all be nullptr
at the end of the example.
#include <algorithm>
#include <iterator>
#include <memory>
#include <vector>
int main()
{
std::vector<std::shared_ptr<int>> sharedV;
std::vector<std::unique_ptr<int>> uniqueV;
uniqueV.emplace_back(std::make_unique<int>(42));
std::move(uniqueV.begin(), uniqueV.end(), std::back_inserter(sharedV));
return 0;
}
To add on top of François Andrieux's answer , std::vector
has a ranged insert()
member function. Just passing in the iterators to your vector of unique_ptr
won't work, but there's a way to convert the dereferencing of those iterators from lvalues to xvalues: std::move_iterator
and its corresponding factory function: std::make_move_iterator
:
sharedV.insert(sharedV.end(),
std::make_move_iterator(uniqueV.begin()),
std::make_move_iterator(uniqueV.end()));
The reason this may be more efficient than using std::back_inserter
is that insert()
will know up front what the resulting size will be, so only at most one allocation needs to be done and then none of the actual insertions need to do size checks.
This is so awkward to write that I would suggest range-based overloads of this named extend()
:
template <class T, class Range>
void extend(std::vector<T>& dst, Range&& range) {
using std::begin; using std::end;
if constexpr (std::is_lvalue_reference<Range>{}) {
dst.insert(dst.end(), begin(range), end(range));
} else {
dst.insert(dst.end(),
std::move_iterator(begin(range)), std::move_iterator(end(range)));
}
}
That's C++17, but is easily doable in C++14. Just takes more typing. You would then write:
extend(sharedV, std::move(uniqueV));
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.