为什么我必须在右值引用上调用move ?

Why do I have to call move on an rvalue reference?

本文关键字:引用 调用 move 为什么      更新时间:2023-10-16

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

正如skypjack正确注释的那样,通过名称访问对象总是导致左值引用。

这是一个安全功能,如果你仔细考虑一下,你会意识到你很喜欢它。

如您所知,std::move只是将左值引用强制转换为右值引用。如果我们立即使用返回的r值引用(即未命名),那么它仍然是一个r值引用。

这意味着r值只能在代码中提到move(x)的地方使用。从代码读者的角度来看,现在很容易看出x的状态在哪里变为undefined。

:

 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的措辞)。这可以在move赋值方法中使用。

所以最后回答你的问题为什么mkme = mvme_rv不调度到T& operator=(const T&&):它是因为mvme_rv不是一个临时对象(又名xvalue)。


关于xvalues的更多信息:http://en.cppreference.com/w/cpp/language/value_category