3

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?

2
  • So it would be desirable to use std::shared_ptr instead Do these things need to participate in ownership? If not you can pass the vector by reference. Commented Aug 24, 2017 at 14:30
  • @NathanOliver Desirable for some specific case that we would want to share ownership of these pointers. Don't be overzealous :) Commented Aug 24, 2017 at 14:34

2 Answers 2

10

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;
}
Sign up to request clarification or add additional context in comments.

3 Comments

Would suggest the [frustratingly] more verbose but also more efficient sharedV.insert(sharedV.end(), std::make_move_iterator(uniqueV.begin()), std::make_move_iterator(uniqueV.end()))
@Barry I was not aware of std::make_move_iterator. Very interesting. I would be uncomfortable appending that solution to my answer, I feel it deserves to be a distinct answer.
Ok, if you insist :)
9

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));

5 Comments

I find it far more readable to first resize the target vector and then just the move algorithm (or a for loop). That should be similarly efficient in this case.
Implementors could make this same optimization with back_inserter, right?
@GManNickG How? By adding overloads of every algorithm taking an output iterator? It's possible I guess, but doesn't seem particularly practical.
@Barry: I was thinking if the iterators are random access iterators, and the output iterator is a back_inserter, the implementation can additionally check if the container in the back_inserter can reserve and if so, call reserve(std::distance(begin, end)). Then pass on like normal. It should all be doable at compile-time. Glancing at my favorite implementation, it doesn't seem to do this, though.
Although on second thought, the permitted operations for move may just flat-out prohibit this w.r.t exceptions. Capacity changing for nothing might be undesirable.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.