《深入理解C++11》讀書筆記——右值引用

左值右值的定義
左值:在作用域內可以持有的變量
右值:在下一行即消亡的臨時變量

右值引用:可以持有即將消亡的臨時變量的引用,用 T&& ref_name 表示
假設存在int i以下代碼不會編譯不出錯的是 (1)(3)(4)
(1) int && ref = i * 3
(2) int & ref = i * 3 // 非常引用不能引用未聲明的臨時變量,未聲明的臨時變量屬於常量
(3) const int & ref = i * 3 // 常引用
(4) int & ref = i
(5) int && ref = i // i是左值,右值引用不能引用

右值引用相關應用:
1.避免返回時,臨時變量的創建和拷貝
在標準的C++98實現中T b = RetrunValue()返回過程中會發生兩次拷貝,一次發生在return前,局部變量拷貝到臨時變量,另一次發生在臨時變量拷貝給b。
爲了避免臨時變量拷貝。
在C++98中經常採用“常引用”引用臨時對象:
定義函數 T ReturnValue()
const T& b = RetrunValue();
在C++11可以使用右值引用
T&& b = RetrunValue();

實時上,當函數返回直接返回臨時變量時,編譯器對返回變量做了優化,將接收返回值變量直接創建在函數的臨時變量的棧上,從而避免了臨時變量的創建;這就是編譯器的返回優化(RVO)或者NRVO
https://www.ibm.com/developerworks/community/blogs/5894415f-be62-4bc0-81c5-3956e82276f3/entry/RVO_V_S_std_move?lang=en
g++ 編譯器加上-fno-elide-constructors選項去掉編譯器優化

2.std::move
該函數的主要作用是將一個左值,變成右值,這個操作有風險,在使用std::move()將左值轉換爲右值之前,應確保左值不會被繼續使用否則會引發異常。
原封不動返回函數內的臨時變量對象,沒有函數內的返回前拷貝,也沒有函數外的臨時變量拷貝(上面舉的例子只是避免了函數外的臨時變量拷貝)

T && ReturnValue()
{
	T obj;
	return std::move(obj); // obj的生命週期即將結束,此時將其轉右值安全
}

T&& obj = ReturnValue(); // 此處obj和函數內的obj是同一個對象
還有一個典型的應用場景是實現高性能的Swap函數

template <class T>
void swap(T& a, T&b)
{
	T tmp(move(a));
	a = move(b);
	b = move(tmp);
}

3.移動構造函數
所謂移動構造函數就是一個接受右值的構造函數,好處對於資源管理的對象,移動構造函數可以避免了對象內的大內存拷貝(譬如堆上的內存)。

class HugeObj
{
	HugeObj(){};
	HugeObj(const HugeObj& obj){};
	// 移動構造函數
	HugeObj(HugeObj&& obj):ptr(obj.ptr) // 截取堆內存
	{
   		obj.ptr = NULL;
	}
	~HugeObj()
	{
  		delete ptr;
	}
private:
	HugeObjPtr* ptr;
}

注意到在C++11的環境下,函數return對象時或者有臨時變量時,如果定義了移動構造函數,優先會調用移動構造函數其次纔是拷貝構造函數

4.引用摺疊
所謂引用摺疊一句話概括就是在模板函數下,傳遞右值總是按右值引用處理,傳遞左值則按左值引用處理

5.完美轉發

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章