提问者:小点点

为什么我必须对rvalue引用调用move?


在下面的代码中,为什么第一个调用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
}

共2个答案

匿名用户

正如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