天天看點

C++ 0x 之移動語義和傳導模闆實作原理

文 / 李博(光宇廣貞)

       《C++ 0x 之左值與右值》文中提到 std::forward() 和 std::move()。本文開頭對之補充一句:

    在操作函數傳回值或函數參數時,匿名左值仍然為左值,左值可以具名;匿名右值仍然為右值,右值一旦具名成功,立即轉變為左值。

       舉一個例子。使用 std::move() 方法向 Outer 傳遞右值後,使用 std::forward() 保證調用右值重載,而直接通路參數 t 的調用綁定到了左值重載。見下圖:

C++ 0x 之移動語義和傳導模闆實作原理

       是以,操作右值引用不能直接操作其變量名,否則将使右值引用具名,進而轉為左值引用。操作右值引用必須使用 std::move()、std::forword() 等方法。将兩個方法的實體展開如下:

C++ 0x 之移動語義和傳導模闆實作原理

       這兩個 identity 和 Remove_Reference 是幹嘛用的?若不要它們,直接用 T&& 做為 forward 裡 arg 的類型或 move 裡傳回類型呢?

       不行。首先說 forward 方法。重新看如下代碼:

       template < typename T > void Outer ( T&& t )

       {

              Inner ( std::forward<T> ( t ) );

       }

       在《C++ 0x 之左值與右值》文中提到,我們使用 forward 的目的是保證參數的左右值性和隻讀性的準确傳導。若不使用 identity 而隻使用 T&& 的話,調用 std::forward<T>(t) 仍然沒有問題;而當調用 std::forward(t),即不指明模闆參數類型時,T&& 将由 t 推導,問題便來了。注意到 t 在傳參數時,是點名調用,使 t 具名,故而無論原來 t 是左值還是右值,此刻都将視為左值,進而 T 被推導為左值引用,且 T&& 歸化為左值引用,于是 forward 方法以左值引用類型接收參數 t,并以左值作為其傳回類型。如是便違背了 forward 的本意。

       而使用 identity 後,type 被指定為 T。注意這裡,“::”算符就像一面牆,擋住了類型推導看見其左側。是以,編譯器不會認為 arg 參數是需要類型推導的,是被 identity::type 指定類型的(當然也強制使用 forward 時要注明模闆參數,否則将無從推導)。這就保證了 forward 總是以右值引用類型接收參數 t。前文已經提到右值引用類型參數可以保留實參的一切資訊。如是保證了 forward 的本意的實作。

       最後,move 方法裡面的 Remove_Reference 就好解釋了。模闆将根據 arg 推導類型,若 arg 是 const Type& 型,則 T 推導為 const Type& 型,而後代入特化模闆,使 type 為 const Type,進而傳回類型為 const Type&& 型。若 arg 是右值類型,則 type 也将歸化為右值引用型。總之,Remove_Reference 模闆類保證傳回類型為右值引用。

       所屬分類:C++

       參考:

       C++ 0x 之 Lambda:賢妻與嬌娃,你娶誰當老婆?聽 FP 如何點化 C++

       從 C++ 模闆元程式設計生産質數看 F# 函數式程式設計思想

       人類最偉大的王安石時代,自古至今乃至不可預見的未來

       天生是兵家——一代神将“蔣方震”

       漢族姓氏與基因——為何有“兩個漢族”?同姓是否同源?随父随母、生男生女是否一樣?

       緬甸攻打果敢漢人與中印邊界對峙的關系——東段麥克馬洪線!

       限次連續若幹同色球機率算法

       Vista 進階掃雷 105 秒到 135 秒各記錄截圖

       做為中國人,以下“曆史片斷”絕對颠覆你的想像!

       王夫人向賈母挑戰