如果我理解正确,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
但不会被移动。
答案 0 :(得分: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上运行/编辑代码。
答案 1 :(得分:10)
move_iterator
的目的是为算法提供其输入的rvalues。
您的示例auto a=std::move(b[n])
不会移动数组中的值,但会将其移出它,这是明智之举。
std::accumulate
中的技巧是operator+ for std::string的定义(请记住,accumulate的默认版本使用operator+
。它对rvalue参数有一个特殊的优化。对于我们的情况,重载次数7是重要的,因为accumulate
使用表达式init + *begin
。这将尝试重用右侧参数的内存。如果这实际上证明是优化并不是很清楚。
答案 2 :(得分: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;
}
如果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)。