1. 程式人生 > >C++ 0x之右值引用

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再支援一點,糾結。

好了,這個東西這麼好用,有必要的話建議大家升級下專案。