如何一次将几个unique_ptrs插入到vector中

时间:2016-09-13 19:30:14

标签: c++ vector unique-ptr

我有一个矢量:

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

并希望在指定位置插入几个新的unique_ptr<int>。有成员函数std::vector::insert(iterator position, size_type n, const value_type& val)但是唉,复制unique_ptr的限制不允许使用这个重载。

我已阅读this question,但这是为了插入已存在于另一个向量中的unique_ptr。我想创造新的。

我意识到我可以用循环来做,例如在向量的开头插入3个新项目:

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

但是我想知道是否有更简洁的方法可以做到这一点,并且可能是预先分配新内存的方法。

编辑以澄清:要添加到向量的项目数是完全随意的 - 我在示例代码中写了3但它可以是任何值,而不一定是编译时已知的值。

4 个答案:

答案 0 :(得分:2)

这里分配了两种不同的内存。在vector中为unique_ptr本身分配了内存(不会很多,只是每个unique_ptr的指针)。然后为每个unique_ptr管理的对象动态分配内存。

您无法预先分配所有内存,因为必须单独分配每个unique_ptr内对象的动态分配内存。

但是如果你想避免由于多次insert调用而重新分配向量的内存,你可以先做一个预留:

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

我怀疑它对n的影响只有3小。

更多的问题是,对于每个insert,向量必须在插入点之后将vector的所有内容移动一个。移动unique_ptr很便宜,但可以加起来。

肯定不是更干净,但你可以使用std::move_backward进行一次移动。将向量调整为所需大小,移动所有元素,然后移动 - 在要插入的元素中分配unique_ptr

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

实现同样目标的另一种可能更简洁的方法是创建自己的自定义前向迭代器,以便在std::vector::insert(const_iterator position, InputIterator first, InputIterator last);的{​​{1}}重载中使用:

insert

答案 1 :(得分:1)

如果您在编译时知道要在向量中添加多少指针,则可以使用如下函数:

#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>();
}

答案 2 :(得分:0)

最干净的方法是使用 move iterator 来移动元素,例如:

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

}

我提供了一个简单的包装函数,它接受一个序列(作为可变参数序列)并使用move_iterator将其插入到向量中。

但您可以使用move_iterator从任何支持移动运算符的容器移动。

答案 3 :(得分:0)

您可以使用generate_n来避免原始循环

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