Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to insert several unique_ptrs into vector at once

I have a vector:

std::vector<std::unique_ptr<int>>

and would like to insert several new unique_ptr<int>'s into it at a specified location. There is the member function std::vector::insert(iterator position, size_type n, const value_type& val) but alas, the restrictions on copying unique_ptr's does not allow the use of this overload.

I have read this question, however that is for inserting unique_ptr's that already exist in another vector. I want to create new ones.

I realize I can do it with a loop, for example to insert 3 new items to the beginning of the vector:

for (int n = 0; n != 3; ++n)
   vec.insert(vec.begin(), std::make_unique<int>(0));

However I'm wondering if there's a cleaner way to do this, and possibly one that allocates the new memory up-front.

Edit for clarification: the number of items to add to the vector is completely arbitrary - I wrote 3 in my example code but it could be any value and not necessarily one that's known at compile time.

like image 847
Carlton Avatar asked Sep 13 '16 19:09

Carlton


People also ask

How do you add all elements from one vector to another?

Use the insert Function to Append Vector to Vector in C++ The insert method is a built-in function of the std::vector container that can add multiple elements to the vector objects. As the first example, we show how to append a given range from one vector to another.

Can I have a vector of Unique_ptr?

This means that you can't make copies of a unique_ptr (because then two unique_ptr s would have ownership), so you can only move it. D.R. Since there can be only one, one should also be able to pass a temporary directly to the vector: vec. push_back(std::unique_ptr<int>(new int(1))); .

Can we append vector?

Appending to a vector means adding one or more elements at the back of the vector. The C++ vector has member functions. The member functions that can be used for appending are: push_back(), insert() and emplace(). The official function to be used to append is push_back().


1 Answers

There is two different kinds of memory being allocated here. There is the memory allocated in the vector for the unique_ptr itself (which will not be very much, just a pointer per unique_ptr). And then the dynamically allocated memory for the object managed by each unique_ptr.

You cannot allocate all the memory up-front as the dynamically allocated memory for the object within each unique_ptr must be allocated separately.

But if you want to avoid reallocations of the memory for the vector due to multiple insert calls you could do a reserve first:

vec.reserve(vec.size() + n);

I doubt it will have any impact for n as small as 3 though.

More of an issue is that for each insert the vector has to move all the contents of the vector after the insert point along by one. Moving unique_ptr is cheap but it could add-up.

It is certainly not cleaner, but you could do the moving yourself once using std::move_backward. Resize the vector to the size required, move all the elements along and then move-assign the unique_ptr in the elements where you want to insert:

auto prev_size = vec.size();
vec.resize(prev_size + 3);
auto prev_end = vec.begin() + prev_size;
std::move_backward(vec.begin(), prev_end, vec.end());
for (int n = 0; n != 3; ++n)
  vec[n] = std::make_unique<int>(0);

Another, perhaps cleaner, way of achieving the same thing is to create your own custom forward-iterator to use in the std::vector::insert(const_iterator position, InputIterator first, InputIterator last); overload of insert:

template<typename T>
struct UniquePtrInserter : std::iterator< 
                           std::forward_iterator_tag,
                           std::unique_ptr<T>,
                           std::ptrdiff_t,  
                           const std::unique_ptr<T>*,
                           std::unique_ptr<T>>{
  int n_;
public:
  explicit UniquePtrInserter<T>(int n = 0) : n_(n) {}
  UniquePtrInserter<T>& operator++() {n_++; return *this;}
  bool operator==(UniquePtrInserter<T> other) const {return n_ == other.n_;}
  bool operator!=(UniquePtrInserter<T> other) const {return !(*this == other);}
  std::unique_ptr<T> operator*() const {return std::make_unique<T>(); }
};

vec.insert(vec.begin(), UniquePtrInserter<int>(0), UniquePtrInserter<int>(3));
like image 96
Chris Drew Avatar answered Sep 21 '22 10:09

Chris Drew