C++ 0x之右值引用
C++ 0x標準出來有一段時間了,一直沒時間看,導致最近看一些程式碼完全不明白是什麼意思了,只好硬著頭皮來看了。
這次先說一個簡單的,右值引用。
關於引用,大家都很清楚了,只會做一標識,而不會拷貝物件,例如:int a = 0; int& b = a; 這個就是傳統的引用,如今也稱為左值引用,一般我們將引用用在函式返回值和引數傳遞上。現在0x標準出來了一個右值引用。為了區別左值引用,就變成右值引用了,用”&&“來表示。
左值引用和右值引用最大的卻別是:右值引用可以繫結到一個臨時的物件(右值)上,而左值引用不行。
int a = 0; int& nLvRef = a; // 左值引用 int&& nRvRef = int(); // 右值引用
上面的是例子是一個左值引用和右值引用的例子。再看下面的例子:
int& nLvRef = int(); // 左值引用, VS報錯:非常量引用的初始值必須是左值 error C2440: “初始化”: 無法從“int”轉換為“int &” int&& nRvRef = int(); // 右值引用
從而可見,我們把一個臨時物件(右值)繫結大了一個右值引用上,而左值引用卻不可以這樣繫結。
右值引用可以繫結一個臨時(匿名)的物件,而臨時的物件沒有必要儲存下來,進行操作的時候我麼你可以”移動(Move)”它,而不是拷貝一個副本下來,這樣就可以減少拷貝副本所帶來的開銷。
例如我們有下面的例子:
void swap(int& a , int& b) { int temp = a; a = b; b = temp; } int _tmain(int argc, _TCHAR* argv[]) { int a = 1; int b = 2; swap(a, b); std::cout << a << " " << b << std::endl; system("pause"); return 0; }
結果我們都很清楚,a和b的值交換了,但是這裡大家注意到,用一個臨時物件來做中間變數,我們做了很多次的物件拷貝。
下來我們使用右值引用中移動的思想來改寫這個swap函式,如下:
void swap(int& a , int& b) { int temp = std::move(a); a = std::move(b); b = std::move(temp); } int _tmain(int argc, _TCHAR* argv[]) { int a = 1; int b = 2; swap(a, b); std::cout << a << " " << b << std::endl; system("pause"); return 0; }
這裡你可能沒看到右值引用操作符,但是卻用了一個std::move(),這個是VS標準庫中自帶的一個移動函式。
我們來用右值引用模擬一下這個標準函式(稍微吐槽下,MS慢慢也接受了boost等公眾認可的東西了,不搞特殊化了,以MS當年的性格,絕對要單獨搞一個另外名字的函式)。
template<typename T> T&& move(T&& a) { return a; } template<typename T> void swap(T& a , T& b) { int temp = move(a); // a被移動到temp,a被清空 a = move(b); // b被移動到a,b被清空 b = move(temp); // temp被移動到a,temp被清空 } int _tmain(int argc, _TCHAR* argv[]) { int a = 1; int b = 2; swap(a, b); std::cout << a << " " << b << std::endl; system("pause"); return 0; }
注意:這裡是移動,並沒有做拷貝,只是將物件移動了一下而已,你可以認為是同一個你還了不同編號的座位。
C++0x中的右值引用算是將引用這塊的東西補全了,雖然左值引用也很好用,但是大家對他的效率以及臨時物件的處理上不是很滿意,而右值引用完美的解決了這個問題。
不過現在大家用的VS編輯器各不一致,想要用右值引用需要VS2010(包含)以上的版本,建議還是用VS2012吧,2010的支援不全面。GCC最先的4.7.3已經全面支援C++ 0x標準了。還是喜歡GCC的果斷,而不像VS一樣拖泥帶水,今天支援一點,sp1再支援一點,糾結。
好了,這個東西這麼好用,有必要的話建議大家升級下專案。