为什么我必须在右值引用上调用 move?
Why do I have to call move on an rvalue reference?
在下面的代码中,为什么第一个调用 mkme = mvme_rv
没有分派给 T& operator=(const T&&)
?
#include <iostream>
#include <string>
#include <vector>
using namespace std;
using T = vector<int>;
int main()
{
T mvme(10, 1), mkme;
T&& mvme_rv = move(mvme); // rvalue ref?
mkme = mvme_rv; // calls T& operator=(const T&)?
cout << mvme.empty(); // 0
mkme = move(mvme_rv); // calls T& operator=(const T&&)?
cout << mvme.empty(); // 1
}
这行代码:
mkme = mvme_rv;
是一个副本,因此将使用副本赋值 (T& operator=(const T&)
)。关于此的关键是两个对象都可以在之后使用,并且应该 - 如果正确实现 - 提供两个相同的对象。
相比之下,这行代码:
mkme = move(mvme_rv);
是移动分配 (T& operator=(const T&&)
)。按照惯例,这将丢弃 mvme_rv
对象(或至少清除它)并使 mkme
基本上成为 mvme_rv
之前的样子。
实际上 T&&
表示临时对象(又名 xvalue)- 不会持久的东西。 std::move
方法基本上将对象转换为临时对象(该措辞归功于@richard-Hodges)。然后可以在移动分配方法中使用它。
所以最后回答你为什么 mkme = mvme_rv
不发送到 T& operator=(const T&&)
的问题:这是 因为 mvme_rv
不是临时对象(又名 xavalue)。
关于 xvalues 的更多信息:http://en.cppreference.com/w/cpp/language/value_category
正如 skypjack 正确评论的那样,通过名称访问对象总是会产生左值引用。
这是一项安全功能,如果您仔细考虑,您会发现自己很高兴。
如您所知,std::move
只是将左值引用转换为右值引用。如果我们立即使用返回的右值引用(即未命名),那么它仍然是一个右值引用。
这意味着只能在代码中提到 move(x)
的地方使用 r 值。从代码 reader 的角度来看,现在很容易看出 x 的状态在哪里变得未定义。
所以:
1: auto x = make_x();
2: auto&& r = std::move(x);
3: // lots of other stuff
35: // ...
54: // ...
55: take_my_x(r);
不起作用。如果是这样,维护代码的人将很难看到(并记住)x(在第 1 行定义)通过第 2 行的引用在第 55 行进入未定义状态。
这更明确:
1: auto x = make_x();
2: //
3: // lots of other stuff
35: // ...
54: // ...
55: take_my_x(std::move(x));
在下面的代码中,为什么第一个调用 mkme = mvme_rv
没有分派给 T& operator=(const T&&)
?
#include <iostream>
#include <string>
#include <vector>
using namespace std;
using T = vector<int>;
int main()
{
T mvme(10, 1), mkme;
T&& mvme_rv = move(mvme); // rvalue ref?
mkme = mvme_rv; // calls T& operator=(const T&)?
cout << mvme.empty(); // 0
mkme = move(mvme_rv); // calls T& operator=(const T&&)?
cout << mvme.empty(); // 1
}
这行代码:
mkme = mvme_rv;
是一个副本,因此将使用副本赋值 (T& operator=(const T&)
)。关于此的关键是两个对象都可以在之后使用,并且应该 - 如果正确实现 - 提供两个相同的对象。
相比之下,这行代码:
mkme = move(mvme_rv);
是移动分配 (T& operator=(const T&&)
)。按照惯例,这将丢弃 mvme_rv
对象(或至少清除它)并使 mkme
基本上成为 mvme_rv
之前的样子。
实际上 T&&
表示临时对象(又名 xvalue)- 不会持久的东西。 std::move
方法基本上将对象转换为临时对象(该措辞归功于@richard-Hodges)。然后可以在移动分配方法中使用它。
所以最后回答你为什么 mkme = mvme_rv
不发送到 T& operator=(const T&&)
的问题:这是 因为 mvme_rv
不是临时对象(又名 xavalue)。
关于 xvalues 的更多信息:http://en.cppreference.com/w/cpp/language/value_category
正如 skypjack 正确评论的那样,通过名称访问对象总是会产生左值引用。
这是一项安全功能,如果您仔细考虑,您会发现自己很高兴。
如您所知,std::move
只是将左值引用转换为右值引用。如果我们立即使用返回的右值引用(即未命名),那么它仍然是一个右值引用。
这意味着只能在代码中提到 move(x)
的地方使用 r 值。从代码 reader 的角度来看,现在很容易看出 x 的状态在哪里变得未定义。
所以:
1: auto x = make_x();
2: auto&& r = std::move(x);
3: // lots of other stuff
35: // ...
54: // ...
55: take_my_x(r);
不起作用。如果是这样,维护代码的人将很难看到(并记住)x(在第 1 行定义)通过第 2 行的引用在第 55 行进入未定义状态。
这更明确:
1: auto x = make_x();
2: //
3: // lots of other stuff
35: // ...
54: // ...
55: take_my_x(std::move(x));