What is move_iterator for

What is move_iterator for

本文关键字:iterator for move is What      更新时间:2023-10-16

如果我理解正确,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]); }

但是,我认为在数组中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但不会移动。

如果我理解正确,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])会怎样?

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

我在这里的回答显示了有人如何将元素从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上运行/编辑。

move_iterator的目的是

为算法提供其输入的右值。

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

std::accumulate诀窍

是 std::string 的 operator+ 定义(请记住,累积的默认版本使用 operator+ 。它对右值参数进行了特殊优化。对于我们的情况,重载数字 7 很重要,因为它accumulate使用表达式 init + *begin .这将尝试重用右侧参数的内存。如果这实际上被证明是一种优化,还不清楚。

http://en.cppreference.com/w/cpp/iterator/move_iterator 是这样说的:

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

大多数(如果不是全部)标准算法接受范围,从范围的开头到结束遍历迭代器,并对取消引用的迭代器执行操作。例如,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)。

但是,如果呼叫是

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)。