为什么我必须调用rvalue引用上的移动?

时间:2016-09-08 08:46:58

标签: c++ move-semantics

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

2 个答案:

答案 0 :(得分:11)

当skypjack正确注释时,通过其名称访问对象总是会产生左值引用。

这是一个安全功能,如果你仔细考虑,你就会意识到你很高兴。

如您所知,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));

答案 1 :(得分:-3)

这行代码:

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