是否有将范围移动到向量中的标准方法?
Posted
技术标签:
【中文标题】是否有将范围移动到向量中的标准方法?【英文标题】:Is there a standard way of moving a range into a vector? 【发布时间】:2012-05-30 00:17:10 【问题描述】:考虑以下将一系列元素插入向量的程序:
vector<string> v1;
vector<string> v2;
v1.push_back("one");
v1.push_back("two");
v1.push_back("three");
v2.push_back("four");
v2.push_back("five");
v2.push_back("six");
v1.insert(v1.end(), v2.begin(), v2.end());
这有效地复制了范围,在目标向量中为整个范围分配了足够的空间,因此最多需要一次调整大小。现在考虑以下程序,它试图将范围移动到向量中:
vector<string> v1;
vector<string> v2;
v1.push_back("one");
v1.push_back("two");
v1.push_back("three");
v2.push_back("four");
v2.push_back("five");
v2.push_back("six");
for_each ( v2.begin(), v2.end(), [&v1]( string & s )
v1.emplace_back(std::move(s));
);
这执行了一个成功的移动,但没有享受到 insert() 在目标向量中预分配空间方面的好处,因此在操作期间可以多次调整向量的大小。
所以我的问题是,是否有插入等效项可以将范围移动到向量中?
【问题讨论】:
如果需要在vector中预分配空间,使用std::vector::reserve
,保留push_back
/emplace_back
。
这将是一个可选优化,仅当范围由随机访问迭代器定义时才有可能。不要指望它。
@rubenvb 是的,我认为这可能就是答案,可惜没有像insert()
这样干净的方法。
虽然在使用std::vector
时总是如此?
@Benj: std::vector
迭代器是随机访问的,但库可能不包含优化。这个问题似乎是在询问从任意未指定范围插入向量,该范围可能没有随机访问迭代器。
【参考方案1】:
您将move_iterator
与insert
一起使用:
v1.insert(v1.end(), make_move_iterator(v2.begin()), make_move_iterator(v2.end()));
24.5.3 中的例子几乎就是这样。
如果 (a) vector::insert
使用 iterator-tag 调度来检测随机访问迭代器并预先计算大小(您假设它在您的示例中复制),您将获得所需的优化,并且(b) move_iterator
保留它所包装的迭代器的迭代器类别(这是标准要求的)。
在一个晦涩的地方:我很确定 vector::insert
可以从源安放(这在这里无关紧要,因为源与目标的类型相同,所以安放与复制/移动相同,但与其他相同的示例相关)。我还没有找到需要这样做的声明,我只是从迭代器对i,j
传递给insert
的要求是T
是EmplaceConstructible
从@ 987654331@.
【讨论】:
很好,不知道make_move_iterator
嗯,我发现也可以使用make_move_iterator
将std::copy_if
转换为std::move_if
的等价物。这非常方便。
很抱歉复活了这个,并在 cmets 中提出了一个问题,但是如果源向量和目标向量是 vectorconst unique_ptr
非常棘手。不能复制也不能移动。 AFAIK 它将把它的参考带到坟墓里,虽然它可能是我错过了一些聪明的把戏。【参考方案2】:
std::move
预分配算法:
#include <iterator>
#include <algorithm>
v1.reserve(v1.size() + v2.size()); // optional
std::move(v2.begin(), v2.end(), std::back_inserter(v1));
以下内容会更灵活:
v1.insert(v1.end(),
std::make_move_iterator(v2.begin()),
std::make_move_iterator(v2.end()));
史蒂夫·杰索普 (Steve Jessop) 提供了确切的背景信息,以及它可能是如何做到的。
【讨论】:
只调整一次目标vector
的机会非常渺茫。
哦,太好了,不知道std::move
的这种形式。虽然我猜back_inserter
仍然可能导致多次调整大小。
@BenVoigt 为什么?我想这取决于实施。 vector::reserve 是你的朋友。让我检查一下标准。
@sehe: back_inserter
不知道它会被调用多少次。 std::move
可以找到,但它无法知道目的地正在插入向量中。
我不认为第一个实际上只能重新分配一次:move
可以看到你有一个随机访问迭代器,所以它可以计算出所需的大小,但它只看到一个 @ 987654331@,不是底层向量,所以没办法预留空间。要捕获这种情况,需要对std::move
进行极其曲折的重载。以上是关于是否有将范围移动到向量中的标准方法?的主要内容,如果未能解决你的问题,请参考以下文章