在下面的代码中,为什么第一个调用mkme=mvme_rv
不分派到T&; 运算符=(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
}
正如skypjack正确指出的那样,通过对象的名称访问对象总是导致一个lvalue引用。
这是一个安全功能,如果你仔细考虑,你会意识到你很高兴它。
如您所知,std::move
只是将L值引用强制转换为R值引用。 如果我们立即使用返回的r值引用(即未命名),那么它仍然是r值引用。
这意味着r值只能在代码中提到move(x)
的位置使用。 从代码阅读器的角度来看,现在很容易看到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&;)
)。 这方面的关键是,这两个对象都可以在之后使用,并且如果实现正确,应该提供两个相同的对象。
相比之下,这行代码:
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