【问题标题】:Is there a back_inserter variant that takes advantage of move?是否有利用 move 的 back_inserter 变体?
【发布时间】:2019-06-15 15:54:13
【问题描述】:

在通用代码中,我试图告诉输出迭代器(实际上是 std::back_inserter_iterator 移动一系列元素。令我惊讶的是,它看起来好像在 move-to-back_inserter 操作中移动了元素。

#include<algorithm> // move
#include<iterator> // back_inserter
#include<vector>
int main(){
    std::vector<std::vector<double> > v(10, std::vector<double>(100));
    std::vector<std::vector<double> > w;

    assert( not v[0].empty() and w.size() == 0 );
    std::copy(v.begin(), v.end(), std::back_inserter(w));
    assert( not v[0].empty() and w.size() == 10 );

    std::move(v.begin(), v.end(), std::back_inserter(w));
    assert( v[0].empty() and w.size() == 20 ); // were v elements moved to w?
}

但是我认为v 的元素不可能真正移动到w,因为毕竟back_inserter 将执行push_back,这意味着复制到w

std::move 的情况下,v 元素似乎更有可能被移动到一个临时位置,然后才复制到w

正确吗?有没有std::back_inserter真的在动?

是否已经有std::back_inserter 的变体可以利用移动/放置?类似于std::back_emplacer

【问题讨论】:

    标签: c++ c++11 copy move push-back


    【解决方案1】:

    std::back_inserter() 是用于创建std::back_insert_iterator 对象的便捷函数模板。

    std::back_insert_iterator 类已经重载了operator= 以采用右值引用 类型,即:

    back_insert_iterator<Container>& operator=(typename Container::value_type&& value);
    

    因此,std::back_insert_iterator 已经准备好利用移动语义。

    【讨论】:

    • 我错过了back_inserter_iteratoroperator=(T&amp;&amp;)push_back 也有T&amp;&amp;
    【解决方案2】:

    如果std::back_insert_iterator 只在构造它的容器上调用push_back,则可以通过查看std::vector::push_back 重载集来回答问题:

    void push_back(const T& value);
    void push_back(T&& value);
    

    所以,这里我们显然有一个右值引用的重载。如果相应的std::back_insert_iterator 在使用右值引用调用时复制其参数,这不是很奇怪吗?确实,我们又有一个非常相似的overload set

    back_insert_iterator<Container>&
        operator=(typename Container::const_reference value);
    back_insert_iterator<Container>&
        operator=(const typename Container::value_type& value);
    

    附加评论

    1) 容器中的结果->push_back(value)
    2) 容器中的结果->push_back(std::move(value))

    回到你原来的问题

    是否有一个 std::back_inserter 真的以某种方式移动?

    是的,这个函数创建的迭代器确实利用了右值引用参数。

    是否已经存在利用 move/emplace 的 std::back_inserter 变体?

    不,但可以实现。见this answer

    【讨论】:

    • 我错过了,我确信push_back 只有const T&amp; 过载。再加上back_inserter_iterator 具有::operator=(T&amp;&amp;) 的事实,使得back_emplacer 的必要性大大降低。
    • @alfC 我猜你对 emplace vs move 感到困惑。内容的移动——因此避免复制和删除——是实现起来要简单得多的功能,您几乎可以假设它在 stl 中无处不在。使用 emplace,您可以提供构造对象所需的参数,并调用特殊的 xxx_emplace() 方法将对象直接构造到容器空间中。当然,这可以是一个移动构造函数,但如果您只想移动,则通常不需要放置。
    • @GemTaylor 是的,但是如果您正在谈论带有值类型的单个参数的 emplace。那么 push_back(move(t)) 并不比 emplace_back(move(t)) 差或好。是吗?
    • @alfC 不应该没有太大的不同,假设您的对象有效地实现了移动语义,并且核心对象本身并没有那么大以至于复制那些简单的成员并不昂贵。例如,移动字符串意味着复制字符串对象本身的至少 24 个字节。 Emplace 避免了这 24 个字节:与分配新字符串、复制内容和删除旧字符串相比没有任何意义,但不是零。
    • 要清楚,将 char* emplace_back 放入字符串向量将保存这个小的额外副本,并清除 24 字节字符串对象本身与相同 char* 的 push_back - 当然字符串有在这两种情况下都被复制到动态内存一次,这是一个更昂贵的操作。但是对比c++98,char*的push_back会创建一个临时字符串,然后dup动态内容,然后删除第一个动态实例。
    【解决方案3】:

    是否已经有 std::back_inserter 的变体可以利用 移动/安置?类似于 std::back_emplacer 的东西?

    这里描述了 back_emplacer: https://stackoverflow.com/a/12131700/7471760

    但它不是标准的一部分。

    另请参阅: Why no emplacement iterators in C++11 or C++14?

    【讨论】:

      猜你喜欢
      • 2012-06-26
      • 2020-04-04
      • 2012-03-27
      • 1970-01-01
      • 2012-02-20
      • 1970-01-01
      • 2017-08-21
      • 2021-12-06
      相关资源
      最近更新 更多