<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    so true

    心懷未來,開創未來!
    隨筆 - 160, 文章 - 0, 評論 - 40, 引用 - 0
    數據加載中……

    深度剖析右值、右值引用、完美轉發等相關概念

    右值、右值引用(T&&)、std::move、完美轉發(std::forward)、通用引用(universal reference)、引用折疊(reference collapsing)這些概念貌似很玄,但其實很它們想做的事情很簡單而且很單一,抓住本質就豁然開朗了。
    上面這些概念的引入完全是為了解決一個問題:盡可能用淺拷貝代替深拷貝;具體該如何實施呢:
    1. 定義move constructor或move operator=,如果你新定義的類沒有move constructor或move operator=,那根本就用不上上面那些高級的概念;
    2. 告訴編譯器什么時候用move constructor(而不是用copy constructor),這就需要借助std::move來把左值轉成右值;
    下面是一些零散的箴言:
    左值:能用&來取地址的東西(有名字的都是左值,不管其是否是const的,而且引用都是左值,因為引用肯定得起個名字去引用另外一個東西);
    右值:不能用&來取地址的東西(說白了就是沒名字的東西,例如函數的返回值);
    const T&是萬能引用,即 const T& t = XXX; //XXX是左值、右值都可以;
    T& t = XXX; //XXX必須是左值;
    T&& t = XXX; //XXX必須是右值;
    const T&& t = XXX; //XXX必須是右值;
    不要把T&&和const T&劃等號,兩個都能hold住臨時變量,給臨時變量續命,但:
    T&& t1 = get_val();
    const T& t2 = get_val();
    t1和t2都是左值,因為都可以用&來取地址,但t1是非const的,因此可以t1.xxx = yyy;去修改t1里的內容(當然如果你定義const T&& t1 = get_val(),那就不能更改了),這樣一來,下面這種代碼受益了:
        string name = get_name(); //因為后面要修改name,所以不能用const string& name
        trim(name);
        可以修改為:
        string&& name = get_name();
        trim(name);
        
    關于右值引用的生命周期(續命能續多久,下面討論的也適用于const T&):
    1. 函數不能返回對局部變量的引用(右值引用也不行),這是鐵的原則;
    2. 引用一旦建立(用一個名字hold住了臨時變量),例如T&& t = get_val();那么這個臨時變量的析構時機就是變量t退出其作用域的時候,因此下面的代碼是錯誤的:
        struct A {
            A(T&& t): _t(t) {};
            T&& _t;
        };
        A* pa = NULL;
        {
            T&& t = get_val();
            pa = new A(t);
        }
        pa->_t; //runtime crash, _t已經析構了
        
    std::move只做一件事:把左值轉成右值,因此T t(std::move(get_val()));不管什么情況下都能保證調用move constructor來構造t;
    c++0x里stl的各種容器都已經新增了move constructor,因此當你希望使用淺拷貝的時候可以借助std::move來達成所愿了,至于什么時候可以用淺拷貝,分兩種情況,有些情況編譯器會默認幫你去調用move constructor,但在你自己新定義的函數或者類成員方法里就需要你自己來判斷了,大的原則就是:這個變量只是傳遞過去就好,中間可以轉好幾道手,但在傳遞過程中不需要做修改;
    完美轉發(std::forward),談到這個就必須得談到通用引用(universal reference)、引用折疊(reference collapsing),其實這幾個概念是捆綁在一起的,而且只用在模板范疇內,而且只是為了解決下面這一種模式:
    template <typename T>
    void func2(T t) {
    }
    template <typename T>
    void func(T&& t) {
        func2(std::forward<T>(t));
    }
    上面這個到底完美在哪里?
    func(get_val()); //這個會導致最后是通過move constructor來構造func2函數里的參數t
    T t0 = get_val();
    T& t1 = t0;
    const T& t2 = get_val();
    T&& t3 = get_val();
    const T&& t4 = get_val();
    func(t0); func(t1); func(t2); func(t3); func(t4); //這5個都會導致最后是通過copy constructor來構造func2函數里的參數t,因為這個5個t*都是左值(有名字的就是左值);
        插播兩個你有可能費解的地方:
        1. func函數的參數是T&&, t0、t1都是左值,不是說不能用左值賦值給右值嗎?這就要提到引用折疊了(詳情可參見最后我推薦的文章),說白了因為func是個模板函數才能這么干;
        2. t3類型是T&&,func的參數也是T&&,把t3傳遞過去,居然還是調用copy contructor,因為t3是右值引用,它引用了一個右值,但它本身卻是左值,到了func函數內部,func函數只能知道它是個左值,了解不到它原本的面貌居然是個右值引用;如果還不理解,再看下下面:
        template <typename T>
        void print(T t) {
        }
        int x1 = 1; int& x2 = x1; const int& x3 = x1; int* x4 = &x1;
        分別用x{1..4}來調用print方法,你肯定知道x1、x2、x3到了print函數里,T就是int,只有x4會被print函數認為T是int*,引用這個概念在模板類型推導時是無法讓模板參數感知到的;此外print(T)和print(T&)是不能同定義的,編譯器會抱怨ambiguous,這個事實也能幫你多一些理解。
    如果想讓上面的這5個最終能通過move constructor來構造func2函數里的參數t,那么只要給每個都用func(std::move(t*))包裝下就可以了;
    再澄清下,func2函數的參數是類型T,并不是引用,因此無論如何都需要生成T的一個新實例,區別就是到底是通過copy constructor還是move constructor來生成了;
    所以,所謂的完美就是體現在了forward能把本來是左值的按左值來傳遞,本來是右值的按照右值來傳遞,具體來說就是作為中間環節的func函數內部實現過程中使用了func2(std::forward<T>(t));這句話,使得可以按照調用方實際的真實情況告知給func2函數如何構造參數t,這有什么好處呢,還是那句話:在適當的時機做合適的引導,讓編譯器幫你調用淺拷貝。
    除此之外,你完全可以忘掉這些玄乎其神的概念,所以只要會套用就可以了。
    以上是一些梗概性或結論性的東西,再結合下面這篇文章,把骨頭之外的血肉補上吧:
    https://codinfox.github.io/dev/2014/06/03/move-semantic-perfect-forward/

    posted on 2018-12-06 11:38 so true 閱讀(523) 評論(0)  編輯  收藏 所屬分類: C&C++

    主站蜘蛛池模板: 亚洲 暴爽 AV人人爽日日碰| 久久精品国产亚洲AV大全| 亚洲av永久综合在线观看尤物| 国产精品青草视频免费播放| 亚洲av无码乱码在线观看野外| 亚洲熟妇成人精品一区| 好大好硬好爽免费视频| 亚洲最大福利视频| 毛片A级毛片免费播放| 亚洲夂夂婷婷色拍WW47| 日韩高清在线免费看| 国产成人精品日本亚洲语音| 国产a不卡片精品免费观看 | 亚洲国产精品成人久久蜜臀| 立即播放免费毛片一级| 亚洲Av无码国产情品久久| 一个人免费观看www视频| 九月丁香婷婷亚洲综合色| 日本亚洲欧洲免费天堂午夜看片女人员| 亚洲国产精彩中文乱码AV| 亚洲一区免费观看| 亚洲色图激情文学| 国产免费一区二区三区VR| 一级毛片免费在线| 亚洲午夜久久影院| 野花高清在线电影观看免费视频| 亚洲精品av无码喷奶水糖心| 亚洲 小说区 图片区 都市| 中文字幕免费在线看| 亚洲美女精品视频| 日韩精品视频免费网址| 黄色网址免费在线观看| 亚洲美女免费视频| 国产青草视频在线观看免费影院| 一级毛片大全免费播放| 久久久久久久久亚洲| 在线jyzzjyzz免费视频| 国产成人精品免费大全| 亚洲国产精品成人精品小说| 日本一道综合久久aⅴ免费| 一区二区视频免费观看|