<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                #Item 23:Understand std::move and std::forward 首先通過了解它們(指std::move和std::forward)不做什么來認識std::move和std::forward是非常有用的。std::move不move任何東西。std::forward也不轉發任何東西。在運行時,他們什么都不做。不產生可執行代碼,一個比特/Users/shikunfeng/Documents/neteaseWork/timeline_15_05_18/src/main/webapp/tmpl/web2/widget/event2.ftl的代碼也不產生。 std::move和std::forward只是執行轉換的函數(確切的說應該是函數模板)。std::move無條件的將它的參數轉換成一個右值,而std::forward當特定的條件滿足時,才會執行它的轉換。這就是它們本來的樣子.這樣的解釋產生了一些新問題,但是,基本上,就是這么一回事。 為了讓這個故事顯得更加具體,下面是C++ 11的std::move的一種實現樣例,雖然不能完全符合標準的細節,但也非常相近了。 ```cpp template<typename T> typename remove_reference<T>::type&& move(T&& param) { using ReturnType = //alias declaration; typename remove_reference<T>::type&&;//see Item 9 return static_cast<ReturnType>(param); } ``` 我為你高亮的兩處代碼(我做不到啊!--菜b的譯者注)。首先是函數的名字move,因為返回的類型非常具有迷惑性,我可不想讓你一開始就暈頭轉向。另外一處是最后的轉換,包含了move函數的本質。正如你所看到的,std::move接受了一個對象的引用做參數(準確的來說,應該是一個universal reference.請看Item 24。這個參數的格式是T&& param,但是請不要誤解為move接受的參數類型就是右值引用,請繼續往下看----菜b譯者注),并且返回指向同一個對象的引用。 函數返回值的"&&"部分表明std::move返回的是一個右值引用。但是呢,正如Item 28條解釋的那樣,如果T的類型恰好是一個左值引用,T&&的類型就會也會是左值引用。為了阻止這種事情的發生,我們用到了type trait(請看Item 9),在T上面應用std::remove_reference,它的效果就是“去除”T身上的引用,因此保證了"&&"應用到了一個非引用的類型上面。這就確保了std::move真正的返回的是一個右值引用(rvalue reference),這很重要,因為函數返回的rvalue reference就是右值(rvalue).因此,std::move就做了一件事情:將它的參數轉換成了右值(rvalue). 說一句題外話,std::move可以更優雅的在C++14中實現。感謝返回函數類型推導(function return type deduction 請看Item 3),感謝標準庫模板別名(alias template)`std::remove_reference_t`(請看Item 9),`std::move`可以這樣寫: ```cpp template<typename T> //C++14; still in decltype(auto) move(T && param) //namespace std { using ReturnType = remove_reference_t<T>&&; return static_cast<ReturnType>(param); } ``` 看起來舒服多了,不是嗎? 因為std::move除了將它的參數轉換成右值外什么都不做,所以有人說應該給它換個名字,比如說叫`rvalue_cast`可能會好些。話雖如此,它現在的名字仍然就是`std::move`.所以記住`std::move`做什么不做什么很重要。它只作轉換,不做move. 當然了,rvalues是對之執行move的合格候選者,所以對一個對象應用std::move告訴編譯器,該對象很合適對之執行move操作,所以std::move的名字就有意義了:標示出那些可以對之執行move的對象。 事實上,rvalues并不總是對之執行move的合格候選者。假設你正在寫一個類,它用來表示注釋。此類的構造函數接受一個包含注釋的std::string做參數,并且將此參數的值拷貝到一個數據成員上.受到Item 41的影響,你聲明一個接收by-value參數的構造函數: ```cpp class Annotation{ public: explicit Annotation(std::string text);//param to be copied, ... //so per Item 41, pass by value }; ``` 但是Annotation的構造函數只需要讀取text的值。并不需要修改它。根據一個歷史悠久的傳統:能使用const的時候盡量使用。你修改了構造函數的聲明,text改為const: ```cpp class Annotation{ public: explicit Annotation(const std::string text);//param to be copied, ... //so per Item 41, pass by value }; ``` 為了避免拷貝text到對象成員變量帶來拷貝代價。你繼續忠實于Item 41的建議,對text應用std::move,因此產生出一個rvalue: ```cpp class Annotation{ public: explicit Annotation(const std::string text) : value(std::move(text))//"move" text into value; this code {...} //doesn't do what it seems to! ... private: std::string value; }; ``` 這樣的代碼通過了編譯,鏈接,最后運行。而且把成員變量value設置成text的值。代碼跟你想象中的完美情況唯一不同的一點是,它沒有對text執行move到value,而是拷貝了text的值到value.text確實被std::move轉化成了rvalue,但是text被聲明為const std::string.所以在cast之前,text是一個const std::string類型的lvalue.cast的結果是一個const std::string的rvalue,但是自始至終,const的性質一直沒變。 代碼運行時,編譯器要選擇一個std::string的構造函數來調用。有以下兩種可能: ```cpp class string{ //std::string is actually a public: //typedef for std::basic_string<char> ... string(const string& rhs); //copy ctor string(string&& rhs); //move ctor }; ``` 在Annotation的構造函數的成員初始化列表(member initialization list),`std::move(text)`的結果是const std::string的rvalue.這個rvalue不能傳遞給std::string的move構造函數,因為move構造函數接收的是非const的std::string的rvalue引用。然而,因為lvalue-reference-to-const的參數類型可以被const rvalue匹配上,所以rvalue可以被傳遞給拷貝構造函數.因此即使text被轉換成了rvalue,上文中的成員初始化仍調用了std::string的拷貝構造函數!這樣的行為對于保持const的正確性是必須的。從一個對象里move出一個值通常會改變這個對象,所以語言不允許將const對象傳遞給像move constructor這樣的會改變次對象的函數。 從本例中你可以學到兩點。首先,如果你想對這些對象執行move操作,就不要把它們聲明為const.對const對象的move請求通常會悄悄的執行到copy操作上。 std::forward的情況和std::move類似,但是和std::move__無條件地__將它的參數轉化為rvalue不同,std::forward在特定的條件下才會執行轉化。std::forward是一個__有條件__的轉化。為了理解它何時轉化何時不轉化,我們來回想一下std::forward的典型的使用場景。最常見的場景是:一個函數模板(function template)接受一個universal reference參數,將它傳遞給另外一個函數(作參數): ```cpp void process(const Widget& lvalArg); //process lvalues void process(Widget&& rvalArg); //process rvalues template<typename T> void logAndProcess(T&& param) //template that passes //param to process { auto now = std::chrono::system_clock::now(); //get current time makeLogEntry("Calling 'process'", now); process(std::forward<T>(param)); } ``` 請看下面對logAndProcess的兩個調用,一個使用的lvalue,另一個使用的rvalue: ```cpp Widget w; logAndProcess(w); //call with lvalue logAndProcess(std::move(w)); //call with rvalue ``` 在logAndProcess的實現中,參數param被傳遞給了函數process.process按照參數類型是lvalue或者rvalue都做了重載。當我們用lvalue調用logAndProcess時,我們自然地期望:forward給process的也是一個lvalue,當我們用rvalue來調用logAndProcess時,我們希望process的rvalue重載版本被調用。 但是就像所有函數的參數一樣,param可能是一個lvalue.logAndProcess內的每一個對process的調用因此想要調用process的lvalue重載版本。為了讓以上代碼的行為表現正確,我們需要一個機制,param轉化為rvalue當且僅當:傳遞給logAndProcess的用來初始化param的參數必須是一個rvalue.這正是std::forward做的事情。這就是為什么std::forward被稱作是一個__條件__轉化(conditional cast):當參數被rvalue初始化時,才將參數轉化為rvalue. 你可能想知道std::forward怎么知道它的參數是否被一個rvalue初始化。比如說,在以上的代碼中,std::forward怎么知道param被一個lvalue或者rvalue初始化?答案很簡單,這個信息蘊涵在logAndProcess的模板參數T中。這個參數傳遞給了std::forward,然后std::forward來從中解碼出此信息。欲知詳情,請參考Item 28。 std::move和std::forward都可以歸之為cast.唯一的一點不同是,std::move總是在執行casts,而std::forward是在某些條件滿足時才做。你可能覺得我們不用std::move,只使用std::forward會不會好一些。從一個純粹是技術的角度來說,答案是肯定的:std::forward是可以都做了,std::move不是必須的。當然,可以說這兩個函數都不是必須的,因為我們可以在任何地方都直接寫cast代碼,但是我希望我們在此達成共識:這樣做很惡心。 std::move的魅力在于:方便,減少了錯誤的概率,而且更加簡潔。舉個栗子,有這樣的一個class,我們想要跟蹤,它的move構造函數被調用了多少次,我們這次需要的是一個static的counter,它在每次move構造函數被調用時遞增。假設該class還有一個std::string類型的非靜態成員,下面是一個實現move constructor(使用std::move)的常見的例子: ```cpp class Widget{ public: Widget(Widget&& rhs) : s(std::move(rhs.s)) { ++moveCtorCalls; } ... private: static std::size_t moveCtorCalls; std::string s; } ``` 如果要使用std::forward來實現同樣的行為,代碼像下面這樣寫: ```cpp class Widget{ public: Widget(Widget&& rhs) //unconventional, : s(std::forward<std::string>(rhs.s)) //undesirable { ++moveCtorCalls; } //implementation ... } ``` 請注意到:首先,std::move只需要一個函數參數(rhs.s), std::forward不只需要一個函數參數(rhs.s),還需要一個模板類型參數(std::string).然后,注意到我們傳遞給std::forward的類型是非引用類型(non-reference),因為這就意味著傳遞的那個參數是一個rvalue(請看Item 28)。綜上,這就意味著std::move比std::forward用起來更方便(至少少敲了不少字),免去了讓我們傳遞一個表示函數參數是否是一個rvalue的類型參數。消除了傳遞錯誤類型(比如說,傳一個std::string&,可以導致數據成員s被拷貝構造,而不是想要的move構造)的可能性。 更重要的是,std::move的使用表明了對rvalue的無條件的轉換,然而,當std::forward只對被綁定了rvalue的reference進行轉換。這是兩個非常不同的行為。std::move就是為了move操作而生,而std::forward,就是將一個對象轉發(或者說傳遞)給另外一個函數,同時保留此對象的左值性或右值性(lvalueness or rvalueness)。所以我們需要這兩個不同的函數(并且是不同的函數名字)來區分這兩個操作。 |要記住的東西| |:--------- | |std::move執行一個無條件的對rvalue的轉化。對于它自己本身來說,它不會move任何東西| |std::forward在參數被綁定為rvalue的情況下才會將它轉化為rvalue| |std::move和std::forward在runtime時啥都不做|
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看