如何在从另一个容器创建 std::vector 时确保一次性内存分配?

问题描述 投票:0回答:2

当我需要从另一个容器的某些元素制作

std::vector
时,例如另一个向量,确保新向量仅在内存中分配一次的最安全方法是:

std::vector<int> v1 = { 1, 2, 3, 4, 5, 6 };
const size_t amount = 3;
std::vector<int> v2;
v2.reserve(amount);
v2.insert(v2.begin(), v1.begin(), v1.begin() + amount);

同时,这段代码比从向量中提取子向量的最佳方法中的非常简单的结构要长得多

std::vector<int> v1 = { 1, 2, 3, 4, 5, 6 };
const size_t amount = 3;
std::vector<int> v2(v1.begin(), v1.begin() + amount);

std::vector<int> v1 = { 1, 2, 3, 4, 5, 6 };
const size_t amount = 3;
std::vector<int> v2 = {v1.begin(), v1.begin() + amount};

问题是:后一种情况是否确保

v2
的内存仅分配一次,或者具体实现可以轻松使用
push_back()
emplace_back()
之类的循环,并在其创建过程中导致许多容器内存重新分配,如副本?

是由标准保证的,还是至少我可以依赖现有的实现,它们不会多次重新分配内存?

我很担心,因为我使用巨大的容器,并希望确保这些更具可读性的代码不会对性能产生影响。

c++ stl stdvector allocation
2个回答
4
投票

我在C++23标准中找到了这段话:

24.3.11.2 构造函数 [vector.cons]

template<class InputIterator>
constexpr vector(InputIterator first, InputIterator last,
const Allocator& = Allocator());
  1. 效果:使用指定的分配器构造一个等于范围
    vector
    [first, last)
  2. 复杂性:仅对 T 的复制构造函数进行 N 次调用(其中 N 是第一个和最后一个之间的距离),并且如果第一个和最后一个迭代器属于前向、双向或随机访问类别,则不进行重新分配。如果它们只是输入迭代器,它会对 T 的复制构造函数进行 N 次调用,并进行 N 次重新分配。

这意味着符合要求的实现的效率至少与手动调用

reserve
+
insert
一样高效。


2
投票

根据 https://en.cppreference.com/w/cpp/container/vector/vector#Complexity,列出的第五个构造函数(用于两个迭代器),对于非输入迭代器,不会发生重新分配。构造函数必须执行相当于

reserve(distance(first, last))
的操作才能符合标准。

因此您的

std::vector<int> v2(v1.begin(), v1.begin() + amount);
将只有一个分配。

© www.soinside.com 2019 - 2024. All rights reserved.