How to insert multiple unique_ptrs into a vector at once

I have a vector:

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

and would like to insert some new unique_ptr<int> into it in a specific place. There is a member function std::vector::insert(iterator position, size_type n, const value_type& val) , but, alas, the restrictions on copying unique_ptr do not allow this overload.

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

I understand that I can do this with a loop, for example, to insert 3 new elements at the beginning of the vector:

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

However, I am wondering if there is a cleaner way to do this and maybe the one that allocates new memory up.

Change for clarification: the number of elements added to the vector is absolutely arbitrary - I wrote 3 in my code example, but it can be any value, and not necessarily the one that was known at compile time.

+6
source share
4 answers

If at compile time you know how many pointers you want to put in the vector, you can use a function such as the following:

 #include<vector> #include<memory> #include<cstddef> template<std::size_t... I> constexpr auto gen(std::index_sequence<I...>) { std::vector<std::unique_ptr<int>> vec; int arr[] = { (vec.push_back(std::make_unique<int>(0)), I)... }; (void)arr; return vec; } template<std::size_t N> constexpr auto create() { return gen(std::make_index_sequence<N>{}); } int main() { auto vec = create<3>(); } 
+1
source

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

You cannot allocate all the memory ahead, since the dynamically allocated memory for the object inside each unique_ptr must be allocated separately.

But if you want to avoid reallocating memory for a vector due to several insert calls, you can make a reserve first:

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

I doubt that this will have any effect on n just 3.

The big problem is that for each insert vector must move all the contents of vector after the insertion point by one. Moving unique_ptr cheap, but it can be augmented.

This is certainly not cleaner, but you can do it yourself using std::move_backward . Resize the vector to the required size, move all the elements along and then move unique_ptr to the elements you want to paste into:

 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, possibly cleaner, way to achieve the same is to create your own custom forward iterator for use in overloading std::vector::insert(const_iterator position, InputIterator first, InputIterator last); 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)); 
+1
source

The cleanest way to do this is to use a move iterator to move items, for example:

 #include <iostream> #include <vector> #include <memory> #include <iterator> using T = std::unique_ptr<int>; void print_vec(const std::vector<T>& vec) { for (auto& x: vec) { std::cout << ' ' << *x; } std::cout << '\n'; } template <typename V, typename ...X> void emplace(V& v, typename V::const_iterator i, X&&... x) { T a[] = { std::forward<X>(x)... }; v.insert(i, std::make_move_iterator(std::begin(a)), std::make_move_iterator(std::end(a))); } int main () { std::vector<T> vec; emplace(vec, vec.begin(), std::make_unique<int>(601), std::make_unique<int>(602), std::make_unique<int>(603)); print_vec(vec); emplace(vec, std::next(vec.begin()), std::make_unique<int>(501), std::make_unique<int>(502), std::make_unique<int>(503)); print_vec(vec); } 

I have provided a simple wrapper function that takes a sequence (like a variational sequence of arguments) and inserts it into a vector using move_iterator .

But you can use move_iterator to move from any container that the move operator will support.

0
source

You can use generate_n to avoid the raw loop

 std::vector<std::unique_ptr<int>> vec; generate_n( back_inserter(vec), 3, []() { return make_unique<int>(); }); 
0
source

All Articles