什么是move_iterator

Min*_*Lin 28 c++ iterator move c++11

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

move_iterator 这里的实现有这条线

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

但是,我认为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"));
Run Code Online (Sandbox Code Playgroud)

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

Ton*_*roy 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])

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

在这里的答案显示了某人如何将元素从a追加/移动list到a 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';
}
Run Code Online (Sandbox Code Playgroud)

输出:

2 1 8 3 4 5 6 
=(X&&) =(X&&) =(X&&) =(X&&) =(X&&) 2 1 -8 -3 -4 -5 -6 
Run Code Online (Sandbox Code Playgroud)

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


pmr*_*pmr 10

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

您的示例auto a=std::move(b[n])不会移动数组中的值,而是将其移出它,这是一件明智的事情.

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


Bul*_*net 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;
}
Run Code Online (Sandbox Code Playgroud)

如果first并且last是正常的迭代器(调用是

std::accumulate(source.begin(), source.end(), std::string("1234"));
Run Code Online (Sandbox Code Playgroud)

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

但是,如果电话是

std::accumulate(std::make_move_iterator(source.begin()), std::make_move_iterator(source.end()), std::string("1234"));
Run Code Online (Sandbox Code Playgroud)

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