什么是move_iterator
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了什么是move_iterator相关的知识,希望对你有一定的参考价值。
如果我理解正确,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
但不会被移动。
如果我理解正确,
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 << '
';
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 << '
';
}
输出:
2 1 8 3 4 5 6
=(X&&) =(X&&) =(X&&) =(X&&) =(X&&) 2 1 -8 -3 -4 -5 -6
代码可以在coliru上运行/编辑。
move_iterator
的目的是为算法提供其输入的rvalues。
您的示例auto a=std::move(b[n])
不会在数组中移动值,而是将其移出它,这是明智之举。
std::accumulate
中的技巧是operator+ for std::string的定义(请记住,accumulate的默认版本使用operator+
。它对rvalue参数有一个特殊的优化。对于我们的情况,重载数字7是重要的,因为accumulate
使用表达式init + *begin
。这将尝试重用右侧参数的内存。如果这实际上证明是优化并不是很清楚。
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;
}
如果first
和last
是正常的迭代器(调用是
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里面,first
和last
是移动迭代器,因此*first
是一个右值引用。这意味着init + *first
改为调用std::operator+(std::string const&, std::string &&)
(重载7)。
以上是关于什么是move_iterator的主要内容,如果未能解决你的问题,请参考以下文章
此 Canon SDK C++ 代码片段的等效 C# 代码是啥?