中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C++11 圖說VS2013下的引用疊加規則和模板參數類型推導規則

發布時間:2020-06-26 15:11:43 來源:網絡 閱讀:240 作者:劉元興 欄目:網絡安全

 

 

背景:
    最近在學習C++STL,出于偶然,在C++Reference上看到了vector下的emplace_back函數,不想由此引發了一系列的“探索”,于是就有了現在這篇博文。

前言:
      右值引用無疑是C++11新特性中一顆耀眼的明珠,在此基礎上實現了移動語義和完美轉發,三者構成了令很多C++開發者拍案叫絕的“鐵三角”(當然不是所有C++開發者)。而在這個“鐵三角”中,有一個無法回避的關鍵細節,那就是引用疊加規則和模板參數類型推導規則。其實,關于這兩個規則,可查到的資料不少,但都有一個特點——簡單(就形式而言)而難懂(就理解而言)(起碼在下這么認為),而且,都沒有例證,僅僅是簡明扼要地交代。而本文恰恰是將這一細節展開,給出演示和證明。誠然,這不是什么開創性的工作,但在下認為也是必不可少的,因為它讓人們對這一關鍵細節了解得更加深入和透徹,另外,從某個角度來說,也填補了空白。

“圖說”是因為:有圖有真相,一目了然,真真切切,不容辯駁。
“VS2013下”是因為:本文所有測試和截圖都來自VS2013,考慮到不同編譯環境下結果可能會略有不同,所以,嚴謹起見,這里加了“VS2013下”。

最后,再說兩點:
    1.本文的行文形式(也可以說是邏輯順序):先結論,再證明,必要時加以解說。

    2.本文使用了大量的截圖,所以讀起來可能會有一種連篇累牘之感(但實際上文章邏輯結構清晰,內容一目了然),給讀者帶來的閱讀上的不適,敬請諒解。

參考資料:
1.維基百科.右值引用   地址:http://zh.wikipedia.org/wiki/%E5%8F%B3%E5%80%BC%E5%BC%95%E7%94%A8(強烈建議大家看)
2.聚客頻道.[C++] 右值引用:移動語義與完美轉發   作者:Dutor   地址:http://ju.outofmemory.cn/entry/105978
3.博客園.【原】C++ 11完美轉發   作者:Hujian   地址:http://www.cnblogs.com/hujian/archive/2012/02/17/2355207.html
4.IBM developerWorks.C++11 標準新特性: 右值引用與轉移語義  作者:李勝利   地址:http://www.ibm.com/developerworks/cn/aix/library/1307_lisl_c11/

正文:

    好了,書歸正文。
 為把問題說清楚,我們先給出以下函數:

C++11 圖說VS2013下的引用疊加規則和模板參數類型推導規則

template<typename T>void f(T&& fpar)//formal parameter 形參{    //函數體}//調用int a=1;int& apar=a;//actual parameter 實參f(apar);

C++11 圖說VS2013下的引用疊加規則和模板參數類型推導規則

 在此基礎上,給出以下表格(設A為基本類型,比如int):

C++11 圖說VS2013下的引用疊加規則和模板參數類型推導規則    表1

說明:
     1.在前面的代碼中,調用前形參fpar被聲明的類型是T&&,調用時傳入的實參apar的類型是int&。
     2.上表中,2、3、4列對應了引用疊加規則,2、3、5列對應了模板參數類型推導規則。
     3.由上表可以知道:
       引用疊加規則的規律是:調用前fpar與apar中有一個是&,結果(即調用后fpar的實際類型)就是&;只有當fpar與apar都是&&時,結果才是&&。
       模板參數類型推導規則的規律是:只有調用前fpar是&&,apar是&時,調用后T的實際類型才是A&,其余3種情況下都是A。(僅就上表,許多資料上不上這樣,
原因

                                     在于紅色部分不一樣,見下面的說明4)
     4.注意到上表中紅色的A,在查閱過的資料中,那個位置是A&,但在下得到的結果卻是A,后面會詳細解釋。
     5.本文所討論的模板參數類型推導,僅是針對上面例子中的T而言的,在C++11里,更經典的類型推導包括auto,decltype等。

    下面逐一給出驗證與說明:

1.驗證規則1

看圖:

C++11 圖說VS2013下的引用疊加規則和模板參數類型推導規則         圖1

    程序中我們設斷點監視變量,我們看到,ra作為int&類實參調用函數wai(因為是外層函數,這里簡單命名為wai,不影響說明問題),調用后,T& w_a變成了int& w_a(即實際類型成了int&),而T w_aa成了int型,即T的類型是int型。這里,調用后形參w_a的實際類型滿足引用疊加規則1(上表中的)。

    關于引用疊加,有兩種理解方式(以上例為例說明):

方式一:

          參數傳遞時,T&與int&“作用”,結果是int&,即T&+int& -> int&。我們將其視為規定,不必解釋。(上表正是以這種方式給出的)

方式二:

        參數傳遞時,將實參ra前面的int&傳給T(即將T換成int&),于是,int& & -> int&(注意int& &的兩個‘&’間有空格,不是右值引用),而將int& & ->

    int&視為規則。基于方式二,上表將變成(不考慮調用后T的類型):

C++11 圖說VS2013下的引用疊加規則和模板參數類型推導規則   表2

其中,第1個”加數“是將T換成的內容,也就是實參前的類型,第2個”加數“是函數參數列表中T后的引用形式,”和“是函數調用后形參的實際形式。下面圖說方式二中規定的正確性:

C++11 圖說VS2013下的引用疊加規則和模板參數類型推導規則            C++11 圖說VS2013下的引用疊加規則和模板參數類型推導規則            C++11 圖說VS2013下的引用疊加規則和模板參數類型推導規則             C++11 圖說VS2013下的引用疊加規則和模板參數類型推導規則

        A& & -> A&                                A& && -> A&                             A&& & -> A&                             A&& && -> A&&

      兩種方式都可以。只不過在下覺得,方式二繞一點,并且,有一種T先變成int&(以圖1所示為例),然后又變成int的莫名其妙之感。所以,在下推薦方式一。

    在T的推導上,我們采用這樣的方式:先由疊加原理得出函數調用后形參的類型,然后將該類型與函數參數列表中形參的類型進行對比、匹配,從而得出T的類型。

如果發現不能匹配,則再次運用疊加規則”推導“出T的類型(我們將在驗證規則3時遇到這種情況)

以圖1中的情況為例:

                                                        T& w_a     (形參列表中的)

                                                      int& w_a     (函數調用后形參的實際類型,由疊加規則決定)

    對比知,T為int型。

2.驗證規則2

圖說:

C++11 圖說VS2013下的引用疊加規則和模板參數類型推導規則        圖2

這似乎已經驗證了規則2,但請看下圖:

C++11 圖說VS2013下的引用疊加規則和模板參數類型推導規則        圖3

    不知是否有人會驚訝,a明明是右值引用,為什么會調用void f(int& lfa)?換句話說,a什么時候變成了左值?

    現在,要告訴大家一個結論(相信許多人都知道,就當在下是重復吧):

     C++標準規定,具名的右值引用被當作左值。[注 6]這一規定的意義在于,右值引用本來是用于實現移動語義,因而需要綁定一個對象的內存地址,然后具有修改這一對象內容的權限,這些操作與左值綁定完全一樣。右值綁定與左值綁定的分野在于確定函數重載時的分辨。對于移動構造成員函數與移動賦值運算符成員函數,其形、實參數結合時是按照右值引用處理;而在這兩個成員函數體內部,由于形參都是具名的,因而都被當作左值,這就可以用該形參來修改傳入對象的內部狀態。另外,右值引用作為xvalue(臨終值)本來是用于移動語義中一次性搬空其內容。具名使其具有更為持久的生存期,這是危險的,因而規定具名后為左值引用,除非程序顯式指定其類型強制轉換為右值引用。
                                                             ——維基百科   地址:http://zh.wikipedia.org/wiki/%E5%8F%B3%E5%80%BC%E5%BC%95%E7%94%A8

     另外,從上圖也可以看出,&&和&的不同可以作為重載標志。

     現在,相信大家也不再驚訝。回過頭來看圖2,我們明白,這個驗證是無效的,ra被當成左值,相當于還是在驗證規則1。那么,怎么辦呢?看下圖

C++11 圖說VS2013下的引用疊加規則和模板參數類型推導規則        圖4

    雖然結論沒有變化,但這種驗證方法是有效的。

    讀者可以在圖4代碼的基礎上,加入圖3中的兩個f函數,然后在main函數中寫f(rt());會得到“右值:1”這樣的輸出。為縮短文章篇幅,這里就不截圖了,請讀者自己驗證。

關于圖4的代碼,說以下幾點:

1.前面說過,具名右值引用按左值引用處理,所以,要達到實驗目的,不能將具名右值引用傳給函數wai(),所以我們傳函數返回值這樣的不具名右值引用。

2.如果我們返回局部變量或是臨時對象的引用(比如在rt()函數中寫int a=1;return a++;,哪怕將int a=1;放在全局,也是不行的,因為a++就是返回++前a的一份拷貝,屬于臨時對象),結果是不正確的(得不到輸出1)。(具體原因在下暫時還不清楚,可能是后邊的代碼執行時將臨時變量的空間覆蓋(重寫)了,在下反匯編單步也沒找出確切的答案(在下匯編學得不怎么樣),這里煩請有知道原因的大牛給出指點,在下感激不盡,先行謝過)

3.就像大家在圖4中看到的那樣,rt()函數中必須將全局變量a強制類型轉換為int&&型再返回,否則,如果寫成return a;,編譯器將產生類似“無法將右值引用綁定到左值”的報錯,原因是具名右值引用a被當做左值。

4.void wai(const T& w_a)中的const不能省,原因是非常量引用(T&)不能接受右值引用。

5.void nei(const int& n_a)中的const也不能省,正如大家在圖4中看到的,在wai()中執行nei(w_a);時,w_a為const int&類型。

簡單說一下T的推導:

                                                          const T& w_a   (參數列表中)

                                                        const int& w_a   (函數調用后w_a的實際類型)

     對比知,T為int型。

     至此,我們可以確定,表1中紅色的A是正確的,A&的說法有誤。

3.驗證規則3

圖說:

C++11 圖說VS2013下的引用疊加規則和模板參數類型推導規則

這里只說一下T的推導。如下:

                                                     T&& w_a      (參數列表中w_a的類型)

                                                    int& w_a      (函數調用后w_a的實際類型)

顯然,此時無法直接匹配。這里我們運用表2(之所以用表2,是因為表2比表1更加直觀)中的第2條A& + && -> A&,推出T為int&類型。

4.驗證規則4

圖說:

C++11 圖說VS2013下的引用疊加規則和模板參數類型推導規則

    這里首先說一點,前邊我們說過,非常量左值引用不能接受右值引用,上圖中,void nei(int& n_a),w_a為int&&類型,那么,rt()中的nei(w_a);是如何通過的呢?

不要忘了,雖然w_a顯示為int&&類型,但它是具名右值引用,所以作為左值引用處理,自然能夠通過。如果我們將void nei(int& n_a)改為void nei(int&& n_a),反而不能通過(w_a被當做int&型,int&&不能接受int&),讀者可以自己試一試。

    再說一下T的推導:

                                                          T&& w_a      (參數列表中w_a的類型)

                                                        int&& w_a      (函數調用后w_a的實際類型,不考慮C++11將其視為int&)

對比,知T為int型。

至此,4個引用疊加規則和相應的模板參數類型推導都說完了,謝謝大家!

后記:

      在下愛鉆研,喜探究,實事求是;但另一方面,又著實才疏學淺,能力有限,所以只能做一些基礎性的工作。但即便如此,也難免有疏漏乃至錯誤之處,這里,在

  下懇請大家批評指正,不吝賜教。您的批評指正就是在下不斷進步的源泉!


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

宁阳县| 洛扎县| 新源县| 监利县| 博客| 澄城县| 那坡县| 招远市| 额济纳旗| 南宫市| 威宁| 确山县| 凉城县| 吴旗县| 石城县| 沙河市| 南阳市| 蒙自县| 财经| 玉环县| 北海市| 唐海县| 库伦旗| 聊城市| 上蔡县| 南部县| 改则县| 策勒县| 平遥县| 永丰县| 集安市| 讷河市| 泰宁县| 兴和县| 屏边| 洞口县| 铜陵市| 浙江省| 浮山县| 青浦区| 广安市|