是否有将范围移动到向量中的标准方法?

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_iteratorinsert 一起使用:

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 的要求是TEmplaceConstructible 从@ 987654331@.

【讨论】:

很好,不知道make_move_iterator 嗯,我发现也可以使用make_move_iteratorstd::copy_if 转换为std::move_if 的等价物。这非常方便。 很抱歉复活了这个,并在 cmets 中提出了一个问题,但是如果源向量和目标向量是 vector> 怎么办?在第一个参数上应用 make_move_iterator 将不起作用,没有它,复制引发错误。我该怎么办? @Evgeny:对我有用,所以我可能误解了你的意思。我建议您提出一个新问题,包括对您不起作用的代码。 @EvgenyDanilenko:是的,const 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 进行极其曲折的重载。

以上是关于是否有将范围移动到向量中的标准方法?的主要内容,如果未能解决你的问题,请参考以下文章

将向量映射到特定范围

是否有将存储在 HDFS 中的数据转换为 JSON 的直接方法?

是否有将 API 密钥和秘密保存到数据库中的最佳实践?

将地图值复制到 STL 中的向量

是否有将谷歌文档转换为谷歌电子表格的功能?

C++ 将单个元素移动到向量中的新位置的最简单最有效的方法