程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++11學習筆記:std::move和std::forward源碼分析

C++11學習筆記:std::move和std::forward源碼分析

編輯:C++入門知識

C++11學習筆記:std::move和std::forward源碼分析


std::move和std::forward是C++0x中新增的標准庫函數,分別用於實現移動語義和完美轉發。
下面讓我們分析一下這兩個函數在gcc4.6中的具體實現。

預備知識

    引用折疊規則:
    X& + & => X&
    X&& + & => X&
    X& + && => X&
    X&& + && => X&&
    函數模板參數推導規則(右值引用參數部分):
    當函數模板的模板參數為T而函數形參為T&&(右值引用)時適用本規則。
    若實參為左值 U& ,則模板參數 T 應推導為引用類型 U& 。
    (根據引用折疊規則, U& + && => U&, 而T&& <=> U&,故T <=> U& )
    若實參為右值 U&& ,則模板參數 T 應推導為非引用類型 U 。
    (根據引用折疊規則, U或U&& + && => U&&, 而T&& <=> U&&,故T <=> U或U&&,這裡強制規定T <=> U )
    std::remove_reference為C++0x標准庫中的元函數,其功能為去除類型中的引用。
    std::remove_reference::type <=> U
    std::remove_reference::type <=> U
    std::remove_reference::type <=> U
    以下語法形式將把表達式 t 轉換為T類型的右值(准確的說是無名右值引用,是右值的一種)
    static_cast(t)無名的右值引用是右值
    具名的右值引用是左值。注:本文中 <=> 含義為“即,等價於“。

    std::move


    函數功能
    std::move(t) 負責將表達式 t 轉換為右值,使用這一轉換意味著你不再關心 t 的內容,它可以通過被移動(竊取)來解決移動語意問題。

    源碼與測試代碼 [cpp] view plaincopy
    1. template
    2. inline typename std::remove_reference<_Tp>::type&&
    3. move(_Tp&& __t)
    4. { return static_cast::type&&>(__t); } [cpp] view plaincopy
      1. #include
      2. using namespace std;
      3. struct X {};
      4. int main()
      5. {
      6. X a;
      7. X&& b = move(a);
      8. X&& c = move(X());
      9. } 代碼說明
          測試代碼第9行用X類型的左值 a 來測試move函數,根據標准X類型的右值引用 b 只能綁定X類型的右值,所以 move(a) 的返回值必然是X類型的右值。測試代碼第10行用X類型的右值 X() 來測試move函數,根據標准X類型的右值引用 c 只能綁定X類型的右值,所以 move(X()) 的返回值必然是X類型的右值。首先我們來分析 move(a) 這種用左值參數來調用move函數的情況。模擬單步調用來到源碼第3行,_Tp&& <=> X&, __t <=> a 。
          根據函數模板參數推導規則,_Tp&& <=> X& 可推出 _Tp <=> X& 。
          typename std::remove_reference<_Tp>::type <=> X 。
          typename std::remove_reference<_Tp>::type&& <=> X&& 。再次單步調用進入move函數實體所在的源碼第4行。static_cast::type&&>(__t) <=> static_cast(a)根據標准 static_cast(a) 將把左值 a 轉換為X類型的無名右值引用。然後我們再來分析 move(X()) 這種用右值參數來調用move函數的情況。模擬單步調用來到源碼第3行,_Tp&& <=> X&&, __t <=> X() 。根據函數模板參數推導規則,_Tp&& <=> X&& 可推出 _Tp <=> X 。
          typename std::remove_reference<_Tp>::type <=> X 。
          typename std::remove_reference<_Tp>::type&& <=> X&& 。再次單步調用進入move函數實體所在的源碼第4行。static_cast::type&&>(__t) <=> static_cast(X())根據標准 static_cast(X()) 將把右值 X() 轉換為X類型的無名右值引用。由9和16可知源碼中std::move函數的具體實現符合標准,
          因為無論用左值a還是右值X()做參數來調用std::move函數,
          該實現都將返回無名的右值引用(右值的一種),符合標准中該函數的定義。

          不光是臨時變量,只要是你認為不再需要的數據,都可以考慮用std::move移動。

          比較有名的std::move用法是在swap中:

          復制代碼
          1 template
          2 void swap(T& a, T& b)
          3 {
          4 T t(std::move(a)); // a為空,t占有a的初始數據
          5 a = std::move(b); // b為空, a占有b的初始數據
          6 b = std::move(t); // t為空,b占有a的初始數據
          7 }
          復制代碼

          總之,std::move是為性能而生的,正式因為了有了這個主動報告廢棄物的設施,所以C++11中的STL性能大幅提升,即使C++用戶仍然按找舊有的方式來編碼,仍然能因中新版STL等標准庫的強化中收益。



          std::forward


          函數功能
          std::forward(u) 有兩個參數:T 與 u。當T為左值引用類型時,u將被轉換為T類型的左值,否則u將被轉換為T類型右值。如此定義std::forward是為了在使用右值引用參數的函數模板中解決參數的完美轉發問題。

          源碼與測試代碼 [cpp] view plaincopy
          1. /// forward (as per N3143)
          2. template
          3. inline _Tp&&
          4. forward(typename std::remove_reference<_Tp>::type& __t)
          5. { return static_cast<_Tp&&>(__t); }
          6. template
          7. inline _Tp&&
          8. forward(typename std::remove_reference<_Tp>::type&& __t)
          9. {
          10. static_assert(!std::is_lvalue_reference<_Tp>::value, "template argument"
          11. " substituting _Tp is an lvalue reference type");
          12. return static_cast<_Tp&&>(__t);
          13. } [cpp] view plaincopy
            1. #include
            2. using namespace std;
            3. struct X {};
            4. void inner(const X&) {cout << "inner(const X&)" << endl;}
            5. void inner(X&&) {cout << "inner(X&&)" << endl;}
            6. template
            7. void outer(T&& t) {inner(forward(t));}
            8. int main()
            9. {
            10. X a;
            11. outer(a);
            12. outer(X());
            13. inner(forward(X()));
            14. }
            15. //inner(const X&)
            16. //inner(X&&)
            17. //inner(X&&) 代碼說明
                測試代碼第13行用X類型的左值 a 來測試forward函數,程序輸出表明 outer(a) 調用的是 inner(const X&) 版本,從而證明函數模板outer調用forward函數在將參數左值 a 轉發給了inner函數時,成功地保留了參數 a 的左值屬性。測試代碼第14行用X類型的右值 X() 來測試forward函數,程序輸出表明 outer(X()) 調用的是 inner(X&&) 版本,從而證明函數模板outer調用forward函數在將參數右值 X() 轉發給了inner函數時,成功地保留了參數 X() 的右值屬性。首先我們來分析 outer(a) 這種調用forward函數轉發左值參數的情況。模擬單步調用來到測試代碼第8行,T&& <=> X&, t <=> a 。
                根據函數模板參數推導規則,T&& <=> X& 可推出 T <=> X& 。
                forward(t) <=> forward(t),其中 t 為指向 a 的左值引用。
                再次單步調用進入forward函數實體所在的源碼第4行或第9行。
                先嘗試匹配源碼第4行的forward函數,_Tp <=> X& 。typename std::remove_reference<_Tp>::type <=> X 。
                typename std::remove_reference<_Tp>::type& <=> X& 。形參 __t 與實參 t 類型相同,因此函數匹配成功。再嘗試匹配源碼第9行的forward函數,_Tp <=> X& 。typename std::remove_reference<_Tp>::type <=> X 。
                typename std::remove_reference<_Tp>::type&& <=> X&& 。形參 __t 與實參 t 類型不同,因此函數匹配失敗。由10與13可知7單步調用實際進入的是源碼第4行的forward函數。static_cast<_Tp&&>(__t) <=> static_cast(t) <=> a。inner(forward(t)) <=> inner(static_cast(t)) <=> inner(a) 。outer(a) <=> inner(forward(t)) <=> inner(a)
                再次單步調用將進入測試代碼第5行的inner(const X&) 版本,左值參數轉發成功。然後我們來分析 outer(X()) 這種調用forward函數轉發右值參數的情況。模擬單步調用來到測試代碼第8行,T&& <=> X&&, t <=> X() 。根據函數模板參數推導規則,T&& <=> X&& 可推出 T <=> X 。
                forward(t) <=> forward(t),其中 t 為指向 X() 的右值引用。
                再次單步調用進入forward函數實體所在的源碼第4行或第9行。先嘗試匹配源碼第4行的forward函數,_Tp <=> X 。typename std::remove_reference<_Tp>::type <=> X 。
                typename std::remove_reference<_Tp>::type& <=> X& 。形參 __t 與實參 t 類型相同,因此函數匹配成功。再嘗試匹配源碼第9行的forward函數,_Tp <=> X 。typename std::remove_reference<_Tp>::type <=> X 。
                typename std::remove_reference<_Tp>::type&& <=> X&& 。形參 __t 與實參 t 類型不同,因此函數匹配失敗。由25與28可知22單步調用實際進入的仍然是源碼第4行的forward函數。
                static_cast<_Tp&&>(__t) <=> static_cast(t) <=> X()。inner(forward(t)) <=> inner(static_cast(t)) <=> inner(X())。outer(X()) <=> inner(forward(t)) <=> inner(X())
                再次單步調用將進入測試代碼第6行的inner(X&&) 版本,右值參數轉發成功。
                由17和32可知源碼中std::forward函數的具體實現符合標准,
                因為無論用左值a還是右值X()做參數來調用帶有右值引用參數的函數模板outer,
                只要在outer函數內使用std::forward函數轉發參數,
                就能保留參數的左右值屬性,從而實現了函數模板參數的完美轉發。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved