




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
1、畢業(yè)設(shè)計(jì)外文資料翻譯學(xué) 院: 專業(yè)班級: 學(xué)生姓名: 學(xué) 號: 指導(dǎo)教師: 外文出處:Effective Modern C+American. HYPERLINK /s?wd=author%3A%28Meyers%2C%20Scott%20%28Scott%20Douglas%29%29%20&tn=SE_baiduxueshu_c1gjeupa&ie=utf-8&sc_f_para=sc_hilight%3Dperson t _blank S Meyers 附 件:1.HYPERLINK l _Java技術(shù)介紹外文資料翻譯譯文; 2.HYPERLINK l _Java_Technical_D
2、escription外文原文 指導(dǎo)教師評語: 該英文資料選擇合理,翻譯準(zhǔn)確度較高,譯文較為流暢。翻譯工作認(rèn)真細(xì)致,嚴(yán)格按照規(guī)定,翻譯材料能與原文保持一致,能正確的表達(dá)出原文的意思,希望能夠?qū)⒓?xì)節(jié)部分處理得更加得當(dāng)。簽名: 2015 年 10 月 14 日1.外文資料翻譯譯文右值引用 移動(dòng)語義和完美轉(zhuǎn)發(fā)當(dāng)你剛開始學(xué)習(xí)移動(dòng)語義和完美轉(zhuǎn)發(fā)時(shí),它們看起來并不復(fù)雜:移動(dòng)語義使編譯器可以使用低消耗的移動(dòng)代替高消耗的拷貝操作,就像拷貝構(gòu)造函數(shù)和拷貝賦值運(yùn)算符讓你控制對象的拷貝一樣,移動(dòng)構(gòu)造函數(shù)和移動(dòng)賦值運(yùn)算符讓你控制移動(dòng)。同時(shí),移動(dòng)引入了只支持移動(dòng)的類型,例如std:unique_ptr, std:futr
3、ue,和std:thread。完美轉(zhuǎn)發(fā)讓我們可以寫出接受任意數(shù)量參數(shù)的函數(shù)模板。并將這些參數(shù)轉(zhuǎn)發(fā)給接受接受參數(shù)數(shù)量與傳給轉(zhuǎn)發(fā)函數(shù)的參數(shù)數(shù)量相同的函數(shù)。右值引用就像膠水一樣講兩個(gè)不相關(guān)的特性結(jié)合在一起,它正是使移動(dòng)語義和完美轉(zhuǎn)發(fā)成為可能的底層語言機(jī)制。你對這些特性越發(fā)熟悉,你就越會(huì)意識到你剛開始的印象只不過是冰山一角。移動(dòng)語義、完美轉(zhuǎn)發(fā)和右值引用的世界遠(yuǎn)比它們看起來要復(fù)雜的多。例如,std:move不移動(dòng)任何東西,完美轉(zhuǎn)發(fā)也并不完美。移動(dòng)操作并不總比拷貝操作性能好;就算移動(dòng)操作性能更好,它們的性能也不總是像預(yù)期的那樣好,并且它們不總在可移動(dòng)的上下文中調(diào)用。無論你如何深入理解這些特性,仍然會(huì)有意外
4、的情況發(fā)生。幸運(yùn)的是,它們的復(fù)雜度是有限的,本章會(huì)探究它們背后深層的機(jī)制,這部分C+將會(huì)十分好理解。你會(huì)知道std:move和std:forward的使用慣例,掌握type&模糊不清的特質(zhì),理解移動(dòng)操作令人驚訝的多種行為背后的原因。一切都將水到渠成, 到那時(shí),你又回到了剛開始的時(shí),移動(dòng)語義、完美轉(zhuǎn)發(fā)和右值引用依舊直截了當(dāng),但這次它們會(huì)一直這樣。在本章的條款中,一件非常重要的事是牢牢記住參數(shù)始終是一個(gè)左值,即使傳入的類型是右值引用(如果這讓你驚訝,請?jiān)倏匆幌碌?頁對左值和右值的概述)。條款23 理解std:move和std:forward更好地了解std:move和std:forward的方法是
5、先了解它們不做什么。std:move不移動(dòng)任何東西,std:forward也并不轉(zhuǎn)發(fā)任何東西。在運(yùn)行時(shí),它們都不做任何事,甚至一行可執(zhí)行的代碼都不產(chǎn)生。std:move和std:forward僅僅是執(zhí)行類型轉(zhuǎn)換的函數(shù)(實(shí)際上是函數(shù)模板)。std:move無條件地將它的參數(shù)轉(zhuǎn)換為右值,而std:forward只在一定條件下才執(zhí)行轉(zhuǎn)換。就是這樣,這樣解釋帶來了新的疑問,不過這樣已經(jīng)大致概括了內(nèi)容。為了更具體得解釋,我們來看一個(gè)C+11中的sd:move的實(shí)現(xiàn)示例template /在命名空間std中typename remove_reference:type&move(T& param) usin
6、g ReturnType = /別名聲明 typename remove_reference:type&; /參見條款9 return static_cast(param);我標(biāo)出了代碼中的兩處,一處是函數(shù)名,因?yàn)榉祷仡愋褪址彪s,我不想讓你在這復(fù)雜的地方浪費(fèi)時(shí)間。另一處是構(gòu)成了這個(gè)函數(shù)精髓的類型轉(zhuǎn)換。正如你所看到的std:move接受一個(gè)對象的引用(更準(zhǔn)確地說是一個(gè)universal引用,參見條款24),并返回這個(gè)對象的引用、函數(shù)返回類型中的&表示std:move返回一個(gè)右值引用,但是正如條款28中解釋得那樣,如果類型T是一個(gè)左值引用,T&就會(huì)變成左值引用。為了防止這種事發(fā)生,type tr
7、ait(見條款9)std:remove_reference用在了T上。這使得&一定會(huì)用在一個(gè)不是引用的類型上。這樣能保證std:move一定返回一個(gè)右值引用。這很重要,因?yàn)楹瘮?shù)返回的右值引用是一個(gè)右值,所以std:move所做所有事情就是將它的參數(shù)轉(zhuǎn)換為右值。順帶一提,在C+14中std:move能被實(shí)現(xiàn)得更簡便一些。多虧了函數(shù)返回值類型推導(dǎo)(參見條款3)以及標(biāo)準(zhǔn)庫的別名模板std:remove_reference_t(參見條款9),std:move能被寫成這樣:templatedecltype(auto) move(T& param) using ReturnType = remove_re
8、ference_t&; return static_cast(param);看上去更簡單了,不是嗎?因?yàn)閟td:move值只轉(zhuǎn)換它的參數(shù)為右值,有人建議給它換個(gè)更好的名字,比如說rvalue_cast。盡管如此,我們?nèi)匀皇褂胹td:move作為它的名字,所以記住std:move做了什么和沒做什么很重要。它做的是轉(zhuǎn)換,沒有做移動(dòng)。當(dāng)然了,右值是移動(dòng)的候選人,所以把std:move應(yīng)用在對象上會(huì)告訴編譯器,這個(gè)對象是可以被移動(dòng)的。這也就是為什么std:move有這樣的名字:能讓指定的對象更容易被移動(dòng)。事實(shí)上,右值是移動(dòng)的唯一候選人。假設(shè)你寫了一個(gè)代表注解的類。這個(gè)類的構(gòu)造函數(shù)接收一個(gè)std:str
9、ing的參數(shù)用作注解,并且它拷貝參數(shù)到一個(gè)數(shù)據(jù)成員中。根據(jù)條款41中的信息,你聲明一個(gè)傳值的參數(shù):class Annotation public: explicit Annotation(std:string text); / 要被拷貝的參數(shù) / 根據(jù)條款,聲明為傳值的 .;但是Annotation的構(gòu)造函數(shù)只需要讀取text的值。它不需要修改它。為了符合把const用在任何可以使用的地方的歷史傳統(tǒng),你修改了你的聲明,因此text成為了const的:class Annotation public: explicit Annotation(const std:string text) .;為了避
10、免在拷貝text到數(shù)據(jù)成員的時(shí)候把時(shí)間浪費(fèi)在拷貝操作上,你應(yīng)該保持條款41的建議,并且把std:move用在text上,這樣就產(chǎn)生了一個(gè)右值:class Annotation public: explicit Annotation(const std:string text) :value(std:move(text) / 將text移動(dòng)到value中去;這段代碼 . /做的事情不像看上去那樣 .private: std:string value;代碼能夠編譯,能夠鏈接,能夠執(zhí)行。這段代碼把數(shù)據(jù)成員value的值設(shè)為text的內(nèi)容。這段代碼與你所需要的完美的版本之間,唯一不同之處就是text不
11、是被移動(dòng)到value中去的,它是拷貝過去的。當(dāng)熱,text通過std:move轉(zhuǎn)換成了一個(gè)右值,但是text被聲明為一個(gè)const std:string,所以在轉(zhuǎn)換之前,text是一個(gè)左值const std:string,然后轉(zhuǎn)換的結(jié)果就是一個(gè)右值const std:string,但是一直到最后,const屬性保留下來了??紤]一下const對于編譯器決定調(diào)用哪個(gè)std:string構(gòu)造函數(shù)有什么影響。這里有兩種可能:class string / std:string實(shí)際上是public: / std:basic_string的一個(gè)typedef . string(const string& r
12、hs); / 拷貝構(gòu)造函數(shù) string(string& rhs); / move構(gòu)造函數(shù) .;在Annotation的構(gòu)造函數(shù)的成員初始化列表中,std:move(text)的結(jié)果是一個(gè)const std:string的右值。這個(gè)右值不能傳給std:string的移動(dòng)構(gòu)造函數(shù),因?yàn)閙ove構(gòu)造函數(shù)只接受非const std:string的右值引用。但是,這個(gè)右值能被傳給拷貝構(gòu)造函數(shù),因?yàn)橐粋€(gè)lvalue-reference-to-const(引用const的左值)能被綁定到一個(gè)const右值上去。因此即使text已經(jīng)被轉(zhuǎn)化成了一個(gè)右值,成員初始化列表還是調(diào)用了std:string中的拷貝構(gòu)造
13、函數(shù)。這樣的行為本質(zhì)上是為了維持const的正確性。一般把一個(gè)值移動(dòng)出去就相當(dāng)于改動(dòng)了這個(gè)對象,所以C+不允許const對象被傳給一個(gè)能改變其自身的函數(shù)(比如移動(dòng)構(gòu)造函數(shù))。我們從這個(gè)例子中得到兩個(gè)教訓(xùn)。第一,如果你想要讓一個(gè)對象能被移動(dòng),就不要把這個(gè)對象聲明為const。在const對象上的移動(dòng)請求會(huì)被默認(rèn)地轉(zhuǎn)換成拷貝操作。第二,std:move事實(shí)上沒有移動(dòng)任何東西,它甚至不能保證它轉(zhuǎn)換出來的對象能有資格被移動(dòng)。你唯一能知道的事情就是,把std:move用在一個(gè)對象之后,它變成了一個(gè)右值。std:forward的情況和std:move相類似,但是std:move是無條件地把它的參數(shù)轉(zhuǎn)換成右
14、值的,而std:forward只在確定條件下才這么做。std:forward是一個(gè)有條件的轉(zhuǎn)換。為了理解它什么時(shí)候轉(zhuǎn)換,什么時(shí)候不轉(zhuǎn)換,回憶一下std:forward是怎么使用的。最常見的情況就是,一個(gè)帶universal引用的參數(shù)被傳給另外一個(gè)參數(shù):void process(const Widget& lvalArg); / 參數(shù)為左值void process(Widget& rvalArg); / 參數(shù)為右值template / 把參數(shù)傳給processvoid logAndProcess(T& param) / 的模板 auto now = std:chrono:system_clock
15、:now(); / 取得正確的時(shí)間 makeLogEntry(Calling process, now); process(std:forward(param);考慮一下兩個(gè)logAndProcess調(diào)用,一個(gè)使用左值,另外一個(gè)使用右值:Widget w;logAndProcess(w); / 用左值調(diào)用logAndProcess(std:move(w); / 用右值調(diào)用在logAndProcess內(nèi)部,參數(shù)param被傳給process函數(shù)。process重載了左值和右值兩個(gè)版本。當(dāng)我們用左值調(diào)用logAndProcess的時(shí)候,我們自然是希望這個(gè)左值作為一個(gè)左值被轉(zhuǎn)發(fā)給process,然后
16、當(dāng)我們使用右值調(diào)用logAndProcess時(shí),我們希望右值版本的process被調(diào)用。但是param就和所有的函數(shù)參數(shù)一樣,是一個(gè)左值。因此在logAndProcess內(nèi)部總是調(diào)用左值版本的process。為了防止這樣的事情發(fā)生,我們需要一種機(jī)制來讓param在它被一個(gè)右值初始化(傳給logAndProcess的參數(shù))的時(shí)候轉(zhuǎn)換成右值。這正好就是std:forward做的事情。這也就是為什么std:forward是一個(gè)條件轉(zhuǎn)換:它只把用右值初始化的參數(shù)轉(zhuǎn)換成右值。你可能會(huì)奇怪std:forward怎么知道他的參數(shù)是不是用右值初始化的。舉個(gè)例子吧,在上面的代碼中,std:forward怎么會(huì)知
17、道param是被左值還是右值初始化的呢?簡單來說就是這個(gè)信息被包含在logAndProcess的模板參數(shù)T中了。這個(gè)參數(shù)被傳給了std:forward,這樣就讓std:forward得知了這個(gè)信息。它具體怎么工作的細(xì)節(jié)請參考條款28??紤]到std:move和std:forward都被歸結(jié)為轉(zhuǎn)換,不同之處就是std:move總是執(zhí)行轉(zhuǎn)換,但是std:forward只在有些情況下執(zhí)行轉(zhuǎn)換,你可能會(huì)問我們是不是可以去掉std:move并且在所有的地方都只使用std:forward。從技術(shù)的角度來看,回答是可以:std:forward能做到所有的事情。std:move不是必須的。當(dāng)然,這兩個(gè)函數(shù)函數(shù)都
18、不是“必須的”,因?yàn)槲覀兡茉谑褂玫牡胤綄慶ast,但是我希望我們能同意它們是必須的函數(shù),好吧,真是令人心煩的事。std:move的優(yōu)點(diǎn)是方便,減少相似的錯(cuò)誤,并且更加清晰??紤]一個(gè)類,對于這個(gè)類我們想要記錄它的移動(dòng)構(gòu)造函數(shù)被調(diào)用了多少次。一個(gè)能在移動(dòng)構(gòu)造的時(shí)候自增的static計(jì)數(shù)器就是我們需要的東西了。假設(shè)這個(gè)類中唯一的非static數(shù)據(jù)是一個(gè)std:string,這里給出通常的辦法(也就是使用std:move)來實(shí)現(xiàn)移動(dòng)構(gòu)造函數(shù):class Widget public: Widget(Widget& rhs) : s(std:move(rhs.s) +moveCtorCalls;.priv
19、ate:static std:size_t moveCtorCalls;std:string s;為了用std:forward來實(shí)現(xiàn)相同的行為,代碼看起來像是這樣的:class Widget public: Widget(Wdiget& rhs) /不常見,以及不受歡迎的實(shí)現(xiàn) : s(std:forward(rhs.s) +moveCtorCalls; ;首先注意std:move只需要一個(gè)函數(shù)參數(shù)(rhs.s),而std:forward卻需要一個(gè)函數(shù)參數(shù)(rhs.s)以及一個(gè)模板類型參數(shù)(std:string)。然后注意一下我們傳給std:forward的類型應(yīng)該是一個(gè)非引用類型,因?yàn)槲覀兗s定
20、好傳入右值的時(shí)候要這么編碼(傳入一個(gè)非引用類型,參見條款28)。也就是說,這意味著std:move需要輸入的東西比std:forward更少,還有,它去掉了我們傳入的參數(shù)是右值時(shí)的麻煩(記住類型參數(shù)的編碼)。它也消除了我們傳入錯(cuò)誤類型(比如,std:string&,這會(huì)導(dǎo)致數(shù)據(jù)成員用拷貝構(gòu)造函數(shù)來替換移動(dòng)構(gòu)造函數(shù))的可能。更加重要的是,使用std:move表示無條件轉(zhuǎn)換到一個(gè)右值,然后使用std:forward表示只有引用的是右值時(shí)才轉(zhuǎn)換到右值。這是兩種非常不同的行為。第一個(gè)常常執(zhí)行移動(dòng)操作,但是第二個(gè)只是傳遞(轉(zhuǎn)發(fā))一個(gè)對象給另外一個(gè)函數(shù)并且保留它原始的左值屬性或右值屬性。因?yàn)檫@些行為如此地
21、不同,所以我們使用兩個(gè)函數(shù)(以及函數(shù)名)來區(qū)分它們是很好的主意。你要記住的事std:move執(zhí)行到右值的無條件轉(zhuǎn)換。就其本身而言,它沒有移動(dòng)任何東西。std:forward只有在它的參數(shù)綁定到一個(gè)右值上的時(shí)候,它才轉(zhuǎn)換它的參數(shù)到一個(gè)右值。std:move和std:forward在運(yùn)行期都沒有做任何事情。條款 24: 區(qū)分右值引用和universal引用古人曾說事情的真相會(huì)讓你覺得很自在,但是在適當(dāng)?shù)那闆r下,一個(gè)良好的謊言同樣能解放你。這個(gè)條款就是這樣一個(gè)謊言。但是,因?yàn)槲覀冊诤蛙浖蚪坏?,所以讓我們避開“謊言”這個(gè)詞,換句話來說:本條款是由“抽象”組成的。為了聲明一個(gè)指向T類型的右值引用,你會(huì)
22、寫T&。因此我們可以“合理”地假設(shè):如果你在源代碼中看到“T&”,你就看到了一個(gè)右值引用??上У厥牵鼪]有這么簡單:void f(Widget& param); / 右值引用Widget& var1 = Widget(); / 右值引用auto& var2 = var1; / 不是右值引用templatevoid f(std:vector& param); / 右值引用templatevoid f(T& param); / 不是右值引用事實(shí)上,“T&”有兩個(gè)不同的意思。當(dāng)然,其中一個(gè)是右值引用。這樣引用行為就是你所期望的:它們只綁定到右值上去,并且它們的主要職責(zé)就是去明確一個(gè)對象是可以被移動(dòng)的
23、。“T&”的另外一個(gè)意思不是左值引用也不是右值引用。這樣的引用看起來像是在源文件中的右值引用(也就是,“T&”),但是它能表現(xiàn)得像是一個(gè)左值引用(也就是“T&”)一樣。它這樣的兩重意義讓它能綁定到左值(就像左值引用)上去,也能綁定到右值(就像右值引用)上去。另外,它能綁定到const或非const對象上去,也能綁定到volatile或非volatile對象上去,甚至能綁定到const加volatile的對象上去。它能綁定到幾乎任何東西上去。這樣空前靈活的引用理應(yīng)擁有它們自己的名字,我叫它們universal引用(萬能引用)。universal引用出現(xiàn)在兩種上下文中。最通用的情況是在函數(shù)模板參數(shù)
24、中,就像來自于上面示例代碼的這個(gè)例子一樣:templatevoid f(T& param); / param是一個(gè)universal引用第二個(gè)情況是auto聲明,包括上面示例代碼中的這一行代碼:auto& var2 = var1; / var2是一個(gè)universal引用這兩個(gè)情況的共同點(diǎn)就是它們都存在類型推導(dǎo)。在模板f中,param的類型正在被推導(dǎo),并且在var2的聲明式中,var2的類型正在被推導(dǎo)。把它們和下面的例子(它們不存在類型推導(dǎo),同樣來自上面的示例代碼)比較一下,可以發(fā)現(xiàn),如果你看到不存在類型推導(dǎo)的“T&”時(shí),你能把它視為右值引用:void f(Widget& param); /
25、沒有類型推導(dǎo) / param是右值引用Widget& var1 = Widget(); / 沒有類型推導(dǎo) / param是右值引用因?yàn)閡niversal引用是引用,它們必須被初始化。universal引用的初始化決定了它代表一個(gè)右值還是一個(gè)左值。如果初始化為一個(gè)右值,universal引用對應(yīng)右值引用。如果初始化為一個(gè)左值,universal引用對應(yīng)一個(gè)左值引用。對于那些屬于函數(shù)參數(shù)的universal引用,它在調(diào)用的地方被初始化:templatevoid f(T& param); / param是一個(gè)universal引用Widget w;f(w); / 左值被傳給f,param的類型是 /
26、 Widget&(也就是一個(gè)左值引用)f(std:move(w); / 右值被傳給f,param的類型是 / Widget&(也就是一個(gè)右值引用)要讓一個(gè)引用成為universal引用,類型推導(dǎo)是其必要不補(bǔ)充條件。引用聲明的格式必須同時(shí)正確才行,而且格式很嚴(yán)格。它必須正好是“T&”。再看一次這個(gè)我們之前在示例代碼中看過的例子:templatevoid f(std:vector& param); / param是一個(gè)右值引用當(dāng)f被調(diào)用時(shí),類型T將被推導(dǎo)(除非調(diào)用者顯式地指定它,這種邊緣情況我們不關(guān)心)。但是param類型推導(dǎo)的格式不是“T&”,而是“std:vector&”。按照上面的規(guī)則,排除
27、了param成為一個(gè)universal引用的可能性。因此param是一個(gè)右值引用,有時(shí)候你的編譯器會(huì)很高興地為你確認(rèn)你是否傳入了一個(gè)左值給f:std:vector v;f(v); / 錯(cuò)誤!不能綁定一個(gè)左值到右值 / 引用上去甚至一個(gè)簡單的const屬性的出場就足以取消引用成為universal的資格:templatevoid f(const T& param); / param是一個(gè)右值引用如果你在一個(gè)模板中,并且你看到一個(gè)“T&”類型的函數(shù)參數(shù),你可能覺得你能假設(shè)它是一個(gè)universal引用。但是你不能,因?yàn)樵谀0逯胁荒鼙WC類型推導(dǎo)的存在。考慮一下std:vector中的這個(gè)push_b
28、ack成員函數(shù):templateclass T, class Allocator = allocator /來自c+標(biāo)準(zhǔn)庫class vector public: void push_back(T& x); .;push_back的參數(shù)完全符合universal引用的格式,但是在這個(gè)情況中沒有類型推導(dǎo)發(fā)生。因?yàn)閜ush_back不能存在于vector的特定實(shí)例之外,并且實(shí)例的類型就完全能決定push_back的聲明類型了。也就是說std:vector v;使得std:vector模板被實(shí)例化為下面這樣:class vectorWidget, allocator public: void pus
29、h_back(Widget& x); /右值引用 .;現(xiàn)在你能清楚地發(fā)現(xiàn)push_back沒有用到類型推導(dǎo)。vector的這個(gè)push_back(vector中有兩個(gè)push_back函數(shù))總是聲明一個(gè)類型是rvalue-reference-to-T(指向T的右值引用)的參數(shù)。不同的是,std:vector中和push_back概念上相似的emplace_back成員函數(shù)用到了類型推導(dǎo):templateclass T, class Allocator = allocatorclass vector public: template void emplace_back(Args&. args);
30、 .;在這里,類型參數(shù)Args獨(dú)立于vector的類型參數(shù)T,所以每次emplace_back被調(diào)用的時(shí)候,Args必須被推導(dǎo)。(好吧,Args事實(shí)上是一個(gè)參數(shù)包,不是一個(gè)類型參數(shù),但是為了討論的目的,我們能把它視為一個(gè)類型參數(shù)。)事實(shí)上emplace_back的類型參數(shù)被命名為Args(不是T),但是它仍然是一個(gè)universal引用,之前我說universal引用的格式必須是“T&”。在這里重申一下,我沒要求你必須使用名字T。舉個(gè)例子。下面的模板使用一個(gè)universal引用,因?yàn)楦袷剑ā皌ype&”)是正確的,并且param的類型將被推導(dǎo)(再說一次,除了調(diào)用者顯式指定類型的邊緣情況):t
31、emplate / param是一個(gè)void someFunc(MyTemplateType& param); / universal引用我之前說過auto變量也能是universal引用。更加精確一些,用auto&的格式被推導(dǎo)的變量是universal引用,因?yàn)轭愋屯茖?dǎo)有發(fā)生,并且它有正確的格式(“T&”)。auto universal引用不像用于函數(shù)模板參數(shù)的universal引用那么常見,但是他們有時(shí)候會(huì)在C+11中突然出現(xiàn)。他們在C+14中出現(xiàn)的頻率更高,因?yàn)镃+14的lambda表達(dá)式可以聲明auto&參數(shù)。舉個(gè)例子,如果你想要寫一個(gè)C+14的lambda來記錄任意函數(shù)調(diào)用花費(fèi)的時(shí)間
32、,你能這么做:auto timeFuncInvocation = (auto& func, auto&. param) start timer; std:forward(func) / 用params std:forward(params). / 調(diào)用func ; 停止timer并記錄逝去的時(shí)間。 ;如果你對lambda中“std:forward”的代碼感到困惑,這可能只是意味著你還沒讀過條款33.不要擔(dān)心這件事。在本條款中,重要的事情是lambda表達(dá)式中聲明的auto&參數(shù)。func是一個(gè)universal引用,它能被綁定到任何調(diào)用的對象上去,不管是左值還是右值。params是0個(gè)或多個(gè)u
33、niversal引用(也就是一個(gè)universal引用包),它能被綁定到任何數(shù)量的任意類型的對象上去。結(jié)果就是,由于auto universal引用的存在,timeFuncInvocation能給絕大多數(shù)函數(shù)的執(zhí)行進(jìn)行計(jì)時(shí)。(對于為什么是絕大多數(shù)而不是任意,請看條款30。)把這件事記在心里:我們這整個(gè)條款(universal引用的基礎(chǔ))都是一個(gè)謊言.額,一個(gè)“抽象”!潛在的事實(shí)被稱為引用折疊,這個(gè)話題會(huì)在條款28中專門討論。但是事實(shí)并不會(huì)讓抽象失效。區(qū)分右值引用和universal引用將幫助你更精確地閱讀源代碼(“我看到的T&只能綁定到右值上,還是能綁定到所有東西上呢?”),并且在你和同事討論
34、的時(shí)候,它能讓你避免歧義。(“我在這里使用一個(gè)universal引用,不是一個(gè)右值引用.”)。它也能讓你搞懂條款25和條款 26的意思,這兩個(gè)條框都依賴于這兩個(gè)引用的區(qū)別。所以,擁抱抽象并陶醉于此吧。就像牛頓的運(yùn)動(dòng)定律(學(xué)術(shù)上來說是錯(cuò)誤的)一樣,比起愛因斯坦的相對論(“事實(shí)”)來說它通常一樣好用并且更簡單,universal引用的概念也是這樣,比起工作在引用折疊的細(xì)節(jié)來說,它是更好的選擇。你要記住的事如果一個(gè)函數(shù)模板參數(shù)有T&的格式,并且會(huì)被推導(dǎo),或者一個(gè)對象使用auto&來聲明,那么參數(shù)或?qū)ο缶褪且粋€(gè)universal引用。如果類型推導(dǎo)的格式不是準(zhǔn)確的type&,或者如果類型推導(dǎo)沒有發(fā)生,t
35、ype&就是一個(gè)右值引用。如果用右值來初始化,universal引用相當(dāng)于右值引用。如果用左值來初始化,則相當(dāng)于左值引用。2.外文原文Rvalue References, Move Semantics,and Perfect ForwardingWhen you first learn about them, move semantics and perfect forwarding seem pretty straightforward: Move semantics makes it possible for compilers to replace expensive copying o
36、perations with less expensive moves. In the same way that copy constructors and copy assignment operators give you control over what it means to copy objects, move constructors and move assignment operators offer control over the semantics of moving. Move semantics also enables the creation of move-
37、only types, such as std:unique_ptr, std:future, and std:thread. Perfect forwarding makes it possible to write function templates that take arbitraryarguments and forward them to other functions such that the target function sreceive exactly the same arguments as were passed to the forwarding functio
38、ns.Rvalue references are the glue that ties these two rather disparate features together.Theyre the underlying language mechanism that makes both move semantics and perfect forwarding possible.The more experience you have with these features, the more you realize that your initial impression was bas
39、ed on only the metaphorical tip of the proverbial iceberg. The world of move semantics, perfect forwarding, and rvalue references is more nuanced than it appears. std:move doesnt move anything, for example, and perfect forwarding is imperfect. Move operations arent always cheaper than copying; when
40、they are, theyre not always as cheap as youd expect; and theyre not always called in a context where moving is valid. The construct “type&” doesnt always represent an rvalue reference.No matter how far you dig into these features, it can seem that theres always more to uncover. Fortunately, there is
41、 a limit to their depths. This chapter will take you to the bedrock. Once you arrive, this part of C+11 will make a lot more sense. Youll know the usage conventions for std:move and std:forward, for example. Youll be comfortable with the ambiguous nature of “type&”. Youll understand the reasons for
42、the surprisingly varied behavioral profiles of move operations. All those pieces will fall into place. At that point, youll be back where you started, because move semantics, perfect forwarding, and rvalue references will once again seem pretty straightforward. But this time, theyll stay that way.In
43、 the Items in this chapter, its especially important to bear in mind that a parameter is always an lvalue, even if its type is an rvalue reference. That is, givenvoid f(Widget& w);the parameter w is an lvalue, even though its type is rvalue-reference-to-Widget. (If this surprises you, please review
44、the overview of lvalues and rvalues that begins on page 2.)Item 23: Understand std:move and std:forward.Its useful to approach std:move and std:forward in terms of what they dont do. std:move doesnt move anything. std:forward doesnt forward anything. At runtime, neither does anything at all. They ge
45、nerate no executable code. Not a single byte.std:move and std:forward are merely functions (actually function templates) that perform casts. std:move unconditionally casts its argument to an rvalue, while std:forward performs this cast only if a particular condition is fulfilled. Thats it. The expla
46、nation leads to a new set of questions, but, fundamentally, thats the complete story.To make the story more concrete, heres a sample implementation of std:move in C+11. Its not fully conforming to the details of the Standard, but its very close.template / in namespace stdtypename remove_reference:ty
47、pe&move(T& param)using ReturnType = / alias declaration;typename remove_reference:type&; / see Item 9return static_cast(param);Ive highlighted two parts of the code for you. One is the name of the function, because the return type specification is rather noisy, and I dont want you to lose your beari
48、ngs in the din. The other is the cast that comprises the essence of the function. As you can see, std:move takes a reference to an object (a universal reference, to be precisesee Item 24) and it returns a reference to the same object.The “&” part of the functions return type implies that std:move re
49、turns an rvalue reference, but, as Item 28 explains, if the type T happens to be an lvalue reference, T&would become an lvalue reference. To prevent this from happening, the type trait (seeItem 9) std:remove_reference is applied to T, thus ensuring that “&” is applied to a type that isnt a reference
50、. That guarantees that std:move truly returns an rvalue reference, and thats important, because rvalue references returned from functions are rvalues. Thus, std:move casts its argument to an rvalue, and thats all it does.As an aside, std:move can be implemented with less fuss in C+14. Thanks to func
51、tion return type deduction (see Item 3) and to the Standard Librarys alias template std:remove_reference_t (see Item 9), std:move can be written this way:template / C+14; still indecltype(auto) move(T& param) / namespace stdusing ReturnType = remove_reference_t&;return static_cast(param);Easier on t
52、he eyes, no?Because std:move does nothing but cast its argument to an rvalue, there have been suggestions that a better name for it might have been something like rvalue_cast. Be that as it may, the name we have is std:move, so its important to remember what std:move does and doesnt do. It does cast
53、. It doesnt move.Of course, rvalues are candidates for moving, so applying std:move to an object tells the compiler that the object is eligible to be moved from. Thats why std:move has the name it does: to make it easy to designate objects that may be moved from. In truth, rvalues are only usually c
54、andidates for moving. Suppose youre writing a class representing annotations. The classs constructor takes a std:string parameter comprising the annotation, and it copies the parameter to a data member. Flush with the information in Item 41, you declare a by-value parameter:class Annotation public:e
55、xplicit Annotation(std:string text); / param to be copied, / so per Item 41,; / pass by valueBut Annotations constructor needs only to read texts value. It doesnt need to modify it. In accord with the time-honored tradition of using const whenever possible, you revise your declaration such that text
56、 is const:class Annotation public:explicit Annotation(const std:string text);To avoid paying for a copy operation when copying text into a data member, you remain true to the advice of Item 41 and apply std:move to text, thus producing an rvalue:class Annotation public:explicit Annotation(const std:
57、string text): value(std:move(text) / move text into value; this code / doesnt do what it seems to!private:std:string value;This code compiles. This code links. This code runs. This code sets the data member value to the content of text. The only thing separating this code from a perfect realization
58、of your vision is that text is not moved into value, its copied. Sure, text is cast to an rvalue by std:move, but text is declared to be a const std:string, so before the cast, text is an lvalue const std:string, and the result of the cast is an rvalue const std:string, but throughout it all, the co
59、nstness remains. Consider the effect that has when compilers have to determine which std:string constructor to call. There are two possibilities:class string / std:string is actually apublic: / typedef for std:basic_stringstring(const string& rhs); / copy ctorstring(string& rhs); / move ctor;In the
60、Annotation constructors member initialization list, the result ofstd:move(text) is an rvalue of type const std:string. That rvalue cant be passed to std:strings move constructor, because the move constructor takes an rvalue reference to a non-const std:string. The rvalue can, however, be passed to t
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 室外庭院涂料施工方案
- 機(jī)房 施工方案
- 開工施工方案
- 灘涂錨桿施工方案
- TSHJNXH 0014-2024 火力發(fā)電廠煙氣二氧化碳捕集系統(tǒng)(化學(xué)吸收法)能效評價(jià)方法
- TSHAEPI 003-2022 餐飲油煙在線監(jiān)測(光散射法)與監(jiān)控技術(shù)規(guī)范
- 二零二五年度解除影視制作解除擔(dān)保合同
- 二零二五年度個(gè)人債權(quán)轉(zhuǎn)讓及債務(wù)清收執(zhí)行合作協(xié)議
- 二零二五年度跨境離婚協(xié)議書電子化執(zhí)行合同
- 二零二五年度子女自愿離婚協(xié)議書范本及離婚后子女監(jiān)護(hù)權(quán)
- 2024年實(shí)驗(yàn)小學(xué)大隊(duì)委競選筆試試題題庫
- 普通工安全技術(shù)操作規(guī)程交底注意事項(xiàng)(8篇)
- 2025屆江蘇省十三大市高三沖刺模擬歷史試卷含解析
- 《高等數(shù)學(xué)(第2版)》 高職 全套教學(xué)課件
- 五代十國史料輯存閱讀筆記
- DataOps 實(shí)踐指南 2.0白皮書
- 農(nóng)村宅基地和建房(規(guī)劃許可)申請表
- 2024年鐵嶺衛(wèi)生職業(yè)學(xué)院單招職業(yè)技能測試題庫及答案解析
- 課本劇哈姆雷特劇本
- 供電所班組建設(shè)方案
- 委托處置不良資產(chǎn)協(xié)議(三篇)
評論
0/150
提交評論