1. 程式人生 > >右值引用 移動語義 完美轉發

右值引用 移動語義 完美轉發

1:右值引用

1.1:定義

討論右值引用錢先給出左值和右值的定義: An lvalue is an expression that refers to a memory location and allows us to take the address of that memory location via the & operator.  An rvalue is an expression that is not an lvalue
如果X&是X的左值引用,那麼X&&就是x的右值引用。右值引用的主要用途就是定義move構造來減少物件間拷貝的開銷。其型別分兩種:const右值引用和非const右值引用,非const右值引用只能繫結到非const右值上,比如
	string&& r1 = string("test");(1)
	int&& r2 = 1;(2)
第一行中,右值引用r1繫結到匿名變數,當表示式結束後匿名變數析構,r1是否還有效?對此C++標準有規定 12.2/5
The second context is when a reference is bound to a temporary.117 The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except:

意思是說如果引用(const左值引用,非const右值引用,const右值引用)繫結到臨時物件,那麼這個臨時物件的生命週期會被延長到跟引用的生命週期一樣長。 注意這裡的1並不是常量,而是pure-rvalue,只有const修飾的才是常量。

1.2:universal references

T&&並不是一定表示右值引用,它的引用型別是未定的,即可能是左值有可能是右值。&&實際上是一個未定的引用型別。這個未定的引用型別被scott meyers稱為universal references(可以認為它是種通用的引用型別),如果&&被一個左值初始化的話,它就是一個左值引用;如果它被一個右值初始化的話,它就是一個右值引用。
(1)
template<typename T>
void f(T&& param); //這裡T的型別需要推導,所以&&是一個universal references

(2)
template<typename T>
class Test {
...
Test(Test&& rhs); // 已經定義了一個特定的型別, 沒有型別推斷
... // && 是一個右值引用
};

void f(Test&& param); // 已經定義了一個確定的型別, 沒有型別推斷,&& 是一個右值引用

(3)
template<typename T>
void f(std::vector<T>&& param); 
//這裡既有推斷型別T又有確定型別vector,那麼這個param到底是什麼型別呢?
它是右值引用型別,因為在呼叫這個函式之前,這個vector<T>中的推斷型別已經確定了,所以到呼叫f時沒有型別推斷了。

(4)
template<typename T>
void f(const T&& param);//右值引用
universal references僅僅在T&&下發生,任何一點附加條件都會使之失效,而變成一個右值引用

1.3:reference collapsing rules:

  • A& & becomes A&
  • A& && becomes A&
  • A&& & becomes A&
  • A&& && becomes A&&
特別的,對於模板引數T&&(universal references),如下面程式碼:
template<typename T>
void foo(T&&);
1:如果實參是左值A,則T被推導為A&,函式形參為A& &&,根據引用摺疊規則最終為A&,左值引用。 2:如果實參是右值A,則T被推導為A,函式形參為A&&,即右值引用