2016-09-08 29 views
2

在下面的代码中,为什么不首先拨打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 
} 
+3

'mvme_rv'的类型是对'T'的右值引用,但是当通过名称使用该变量时,它只不过是一个左值。 – skypjack

+2

区分“右值参考”和“右值”。 –

+1

命名表达式总是lvlaues。术语“右值引用”中的“右值”一词描述了*引用*的类型:右值引用是绑定到右值的引用,并且左值引用是绑定到左值(大部分)的引用。在这个意义上,右值引用是一种新的语言功能,它为语言增加了一种通用的右值到左值的转换,但迄今为止还没有这种转换。 –

回答

-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

+0

“std :: move操作符基本上需要一个普通的对象并且撕掉内核”不,它不影响操作数。它不是一个运营商。 – juanchopanza

5

作为skypjack正确地注释,通过其名称访问对象总是导致左值参考。

这是一个安全功能,如果你认为它会通过你会意识到你很高兴它。

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

这意味着只能在提到move(x)的代码中使用r值。从代码阅读器的角度来看,现在很容易看到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)); 
相关问题