我所知道的Move語義與完美轉發

我所知道的Move語義與完美轉發


一、臨時變量效率問題
	
	///////////////////////////////////
	std::vector<int> f()
	{
		std::vector<int> v;
		// stuff
		return v;
	}
	
	std::vector<int> arr(f());
	//////////////////////////////////
	
	這部分代碼做了三件事情:
	1、 創建v
	2、 用v構造f()的返回值
	3、 用f()的返回值構造arr
	
	問題:在此過程中總共構造了3個v的副本,若能將v直接構造在arr處,就可以避免2次
	不必要的拷貝構造過程。怎麼辦呢?
	
	在C++11之前,這種構造開銷通過RVO(返回值優化)來解決,但是對於如下代碼:
	//////////////////////////////////
	std::vector<int> arr;
	arr = fun();
	//////////////////////////////////
	編譯器就無能爲力了。
	
	直到C++11的到來,該問題才被完美解決。
	
二、右值引用
	
	爲了解決臨時變量的效率問題,C++11引入了“右值引用”。在接下來的文章中,我
	將逐步解釋“右值引用”如何成爲美貌與智慧並重、英雄與狹義的化身。
		
	左值、右值是表達式的屬性,而非對象的屬性。左值在表達式結束之後任然存在,而
	右值在表達式結束之後就會析構。
	
	根據右值定義,第一節提到的f()就屬於右值。
	
	爲了說明後續的問題,我將C++11標準的若干規則羅列如下:	
	
	假設有類型T,T&和const T&表達了傳統的左值引用,而T&&表達新引入的右值引用。	
	綁定規則:
	1、const T &和T&&可以綁定到右值;
	2、T&只能綁定左值;
	3、右值優先綁定到T&&;
	
	引用摺疊規則:
	T & & ==> T &
	T & && ==> T &
	T && & ==> T &
	T && && ==> T &&
	
	是的,我目前只能告訴你這些規則,至於爲什麼C++標準這樣定義規則是合理的,我
	還沒法做出有說服力的回答。
	
三、Move 語義


	還記得std::vector的swap方法麼?
	//////////////////////////////////
	std::vector<int> tmp;
	// tmp stuff
	std::vector<int> arr;
	arr.swap(tmp);
	//////////////////////////////////
	
	要是能確定tmp不再有用,那麼將tmp的內容swap過來是非常優雅和高效的手段。實際上,
	Move semantic 正是這種思想。
	
	問題是怎麼表明tmp不再有用?C++11標準庫提供了一個方法模板:
	template<typename T>
	std::remove_reference<T>::type && move(T &&);
	
	對於move的實現放在後邊的章節解釋,目前只需要明白std::move<T>(x)調用返回對x的右值
	引用。	
	
	假設有如下實現的類:
	//////////////////////////////////
	class M
	{
	public:
		M(std::vector<int> && v)	// move constructor
		{
			m_v = std::move(v);
		}
		
		M(const std::vector<int> & v)
		{
			m_v = v;
		}
		
		M& operator=(std::vector<int> && v) // move assignment
		{
			m_v.swap(v);
		}
		
		M& operator=(const std::vector<int> & v)
		{
			m_v.reserve(v.size());			
			std::copy(m_v.begin(), v.begin(), v.end());
		}
		
		std::vector<int> m_v;
		
		//...
	}
	//////////////////////////////////	
	
	模板的第一個構造函數稱爲:move構造函數,形參爲右值引用。相對於第二個傳統的
	拷貝構造函數,它的實現高效、優雅了太多。
	
	考慮下面的調用
	//////////////////////////////////
	M m(f());
	
	std::vector<int> tmp;
	m = tmp;
	m = std::move(tmp);
	//////////////////////////////////	
	根據綁定規則,f()優先綁定到右值引用,因此m將通過move構造函數構造。然後,調用
	普通賦值操作爲m賦值,之後又調用move賦值操作爲m賦值。
	
四、完美轉發
	template<typename T>
	class P
	{
	}
	
	


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