什么是move_iterator

问题描述 投票:26回答:3

如果我理解正确,a=std::move(b)将参考a绑定到b的地址。并且在此操作之后,b指向的内容不能得到保证。

move_iterator here的实施有这条线

auto operator[](difference_type n) const -> decltype(std::move(current[n]))
  { return std::move(current[n]); }

但是,我不认为std::move数组中的元素是有意义的。如果a=std::move(b[n])会发生什么?

以下示例也让我困惑:

std::string concat = std::accumulate(
                             std::move_iterator<iter_t>(source.begin()),
                             std::move_iterator<iter_t>(source.end()),
                             std::string("1234"));

由于concat本身会分配一个连续的内存块来存储结果,这与source不会有任何重叠。 source中的数据将被复制到concat但不会被移动。

c++ c++11 iterator move
3个回答
20
投票

如果我理解正确,a=std::move(b)将参考a绑定到b的地址。并且在此操作之后,b指向的内容不能得到保证。

啊,不:a不一定是参考。上面使用的std::move也授予编译器调用decltype(a)::operator=(decltype(b)&&)的权限(如果它存在):在分配给a时,使用这样的赋值运算符不需要保留b的值,但b仍然必须处于某种理智的状态以便销毁。

但是,我不认为std::move数组中的元素是有意义的。如果a=std::move(b[n])会发生什么?

它有意义......它只是意味着每个数组元素可以被有效地分配/移动到另一个变量,但每个元素只有一次。在它们被移动之后,正确编写的移动构造函数或赋值运算符应该使对象处于有效但未指定的状态,这意味着您通常希望在读取它们之前再次设置它们。

我的answer here显示了某人如何将list中的元素附加/移动到vector。使用当前的C ++标准,您可以直接创建move_iterators。

下面的代码显示了 - 即使使用较旧的编译器/ C ++标准 - 如果要从源迭代器范围中的元素移动,make_move_iterator可以与std::copy一起使用。

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>

struct X
{
    X(int n) : n_(n) { }
    X(const X& rhs) : n_(rhs.n_) { }
    X(X&& rhs) : n_{ rhs.n_ } { rhs.n_ *= -1; std::cout << "=(X&&) "; }
    X& operator=(X&& rhs) { n_ = rhs.n_; rhs.n_ *= -1; std::cout << "=(X&&) "; return *this; }
    int n_;
};

int main()
{
    std::vector<X> v{2, 1, 8, 3, 4, 5, 6};
    std::vector<X> v2{};

    std::copy(v.begin() + 2, v.end(), std::insert_iterator(v2, v2.end()));
    for (auto& x : v)
        std::cout << x.n_ << ' ';
    std::cout << '\n';

    std::copy(std::make_move_iterator(v.begin() + 2), std::make_move_iterator(v.end()), std::insert_iterator(v2, v2.end()));
    for (auto& x : v)
        std::cout << x.n_ << ' ';
    std::cout << '\n';
}

输出:

2 1 8 3 4 5 6 
=(X&&) =(X&&) =(X&&) =(X&&) =(X&&) 2 1 -8 -3 -4 -5 -6 

代码可以在coliru上运行/编辑。


10
投票

move_iterator的目的是为算法提供其输入的rvalues。

您的示例auto a=std::move(b[n])不会在数组中移动值,而是将其移出它,这是明智之举。

std::accumulate中的技巧是operator+ for std::string的定义(请记住,accumulate的默认版本使用operator+。它对rvalue参数有一个特殊的优化。对于我们的情况,重载数字7是重要的,因为accumulate使用表达式init + *begin。这将尝试重用右侧参数的内存。如果这实际上证明是优化并不是很清楚。


9
投票

http://en.cppreference.com/w/cpp/iterator/move_iterator说:

std :: move_iterator是一个迭代器适配器,其行为与底层迭代器(必须至少是一个InputIterator)完全相同,只是解除引用将底层迭代器返回的值转换为右值。

大多数(如果不是全部)接受范围的标准算法,从范围的开头到结束遍历迭代器,并对解除引用的迭代器执行操作。例如,std::accumulate可能实现为:

template <class InputIterator, class T>
T accumulate (InputIterator first, InputIterator last, T init)
{
  while (first!=last) {
    init = init + *first;
    ++first;
  }
  return init;
}

如果firstlast是正常的迭代器(调用是

std::accumulate(source.begin(), source.end(), std::string("1234"));

,然后*first是字符串的左值引用,表达式init + *first将调用std::operator+(std::string const&, std::string const&)(重载1 here)。

但是,如果电话是

std::accumulate(std::make_move_iterator(source.begin()), std::make_move_iterator(source.end()), std::string("1234"));

然后在std :: accumulate里面,firstlast是移动迭代器,因此*first是一个右值引用。这意味着init + *first改为调用std::operator+(std::string const&, std::string &&)(重载7)。

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