本文翻譯自關於右值引用解釋的經典文章,如果英文還可以的話,直接去看英文原文。thbecker.net/articles/rvalue_references/section_01.html
右值引用是c++中的一個特性,並且已經入駐c++11標准,可能大家一開始接觸的時候感覺有點難以理解,但是他的確是很好用的一個玩意兒。
右值引用解決了兩個問題:
1.move語義 2.完美轉發。
我們接下來先簡單介紹一下move語義,但是介紹move語義之前,我們需要先介紹左值和右值的含義。
在C語言中,我們給出左值的定義為:左值是一個表達式e,那麼他既可以出現在賦值操作符的左邊,也可以出現在右邊。右值是:只允許出現在賦值操作符右邊的表達式。
int a = 42; int b = 43; // a b 都是左值 a = b; // ok b = a; // ok a = a * b; // ok // a * b 是右值 int c = a * b; // ok, 右值出現在=右邊 a * b = 42; // error, 右值不允許出現在=左邊
那麼在c++中,左右值的定義也差不多,但是有一點點變化。在c++中,左值是:你可以對它取地址(&)操作的表達式,右值是非左值的表達式。
// 左值: // int i = 42; i = 43; // ok, i 是左值 int* p = &i; // ok, i 是左值 int& foo(); foo() = 42; // ok, foo() 是左值 int* p1 = &foo(); // ok, foo() 是左值 // 右值: // int foobar(); int j = 0; j = foobar(); // ok, foobar() 是右值 int* p2 = &foobar(); // error, 不能取右值表達式的地址 j = 42; // ok, 42 是右值
假設我們有一個類X,他有一個成員函數為一個指針,可能指向某些資源之類的。那麼相應的構造、析構、拷貝等操作都是需要一些特殊的處理工作。
比如我們常見的std::vector,那麼X的拷貝操作就類似下面這樣:
X& X::operator=(X const & rhs) { // [...] // 先收回資源m_pResource // 對rhs的m_pResource指向的資源做拷貝操作 // 然後令m_pResource指向新的內存位置. // [...] }
或者你可能會遇到下面這種代碼:
X foo(); X x; x = foo();
那麼第三句,也就是最後一句要執行三個操作:
1.釋放原來x的資源。
2.克隆foo返回值中的資源給x的資源。
3.銷毀釋放返回值中的資源。
很顯然,這是很低效的做法,更聰明的做法是swap操作,交換x和返回值x`的資源,然後原x的資源會被自動釋放。
換句話說我們想做的就是:
...
swap(m_pResource,rhs.m_pResource);
...
這個就叫做move語義,在c++11中,通過重載可實現這個特性:
X& X::operator=(<???> rhs) { // [...] // swap(m_pResource,rhs.m_pResource); // [...] }
那麼這個???是個什麼類型呢?當然首先它應該是一種引用類型,減少不必要的值拷貝,然後我們還想它和傳統的引用類型分開,因為我們希望當右操作數為右值的時候,調用???這個重載,而當右操作數為左值的時候,調用原始的引用賦值構造,那麼好吧,那我們就把???叫做“右值引用”好了。