C 11 右值引用以及std move

2021-09-07 05:30:25 字數 3675 閱讀 9204

int和int&是什麼?都是型別。int是整數型別,int&則是整數引用型別。相同int&&也是乙個型別。兩個引號&&是c++ 11提出的乙個新的引用型別。記住,這是乙個新的型別。默念

10次吧。假設你記住這個新型別,那麼非常多疑問都能迎刃而解。而且對《effective modern c++》說到的void f(widget&& w)。就非常easy明確w是新型別的乙個值。肯定是乙個左值而不是右值,自然就不用去翻第二頁了。

出現了新型別。就像定義乙個新類一樣。自然有兩件事接著要做:怎樣初始化、函式匹配(依據引數型別匹配函式)。先看後者。

void fun(int &a)

~test()

test(test &t) : p(t.p)//注意這個拷貝建構函式的引數沒有const

private:

char *p;

};int main()

注意這個拷貝建構函式的引數沒有

const。

讀者們,你們會認為上面那個test在拷貝建構函式不高效嗎?差點兒是沒有不論什麼效率上的負擔啊。

類似,也能寫乙個高效的賦值函式。

可是,一般來說我們的拷貝建構函式的引數都是有const的。有const意味著不能改動引數t。上面的**也能夠看到:將t.p賦值nullptr是必須的。

由於t.p不能改動,所以不得不進行深複製。不然將出現經典的淺複製問題。不用說。有const的拷貝建構函式更適合一些,畢竟我們須要從乙個const物件中複製乙份。

在c++ 11之前,我們僅僅能眼睜睜看著重量級的類僅僅能呼叫有const的拷貝建構函式,複製乙個重量級物件。在c++ 11裡面增加了乙個新型別右值引用,那能不能用這個右值引用型別作為建構函式的引數呢?當然能夠啦。畢竟類的構造函式引數沒有什麼特別的要求。

習慣上,我們會稱這種建構函式為移動(move)建構函式,相應的賦值操作則稱為移動(move)賦值函式。

他們的**也非常easy。例如以下:

class test

儘管上面的**在構造b的時候呼叫了移動構造。但明顯上面**一點都不正常,為什麼不直接構造b呢?全然用不著move構造啊。此時可能有讀者會想到這樣乙個用途:我們能夠為乙個暫時物件加上std::move啊,比方operator + 的返回值。實際上這是畫蛇添足的。

由於編譯器會為這個暫時物件當作右值(準確說應該是:將亡值),當然也就自己主動能使用移動構造了。

難道移動構造是屠龍之技?不是的。

移動構造的一大長處是能夠高效地在函式中返回乙個重量級的類。函式返回值會在後面說到。

除了在函式返回值用到外,在函式內部也能夠使用到的。

std::vectorg_ids;//全域性變數

void addids(std::string id)

int main()

int main()

無疑,在main函式中,還須要考慮一下tt物件是乙個test型別還是test&&型別。事實上。大錯早就在fun函式中鑄成了。

返回的僅僅是乙個引用,真身呢?真身已經在fun函式中被摧毀了。meyers早在《effective c++》裡面就告誡過:不要在函式中返回乙個引用。前文也已經說了。右值引用也是乙個引用(型別)! 那返回什麼好呢? 當然是真身啦!  如同以下**:

test fun()

int main()

當函式返回乙個物件時,編譯器會將這個物件看作的乙個右值(準確來說是將亡值)。所以無需在fun函式中。將return t寫成return std::move(t);

當然。實際上t變數的真身還是在fun函式中被摧毀了,但真身裡面有價值的東西都被移走了。

對!就像比克大魔王那樣,臨死前把自己的孩子留下來! 在c++裡面。當然不能生成乙個孩子,可是能夠通過移動建構函式生成乙個暫時物件。把有價值的東西移走。由於不是移動到main函式的tt變數中。僅僅是移動到了暫時物件。所以接下來暫時物件還要進行一次移動,把有價值的東西移動到main函式的tt變數中。這個移動過程無疑是乙個非常好的金蟬脫殼的經典教程。讀者能夠執行一下**,能夠看到整個移動過程。

記住。用

g++編譯的時候要增加

-fno-elide-constructors

選項,禁止編譯器使用rvo優化。

由於這裡的rvo優化比移動構造更省力。

所以假設不禁用,會優先使用rvo,而非移動建構函式。

由於右值引用也是乙個引用型別。所以僅僅能初始化而不能賦值。既然這樣。那僅僅需討論什麼型別的值才幹用於初始化乙個右值引用。一般來說,右值引用僅僅能引用右值、字面值、將亡值。所以問題轉化為:什麼是右值?網上介紹的乙個方法是:要能不能將取位址符號&應用於某個識別符號。假設能就說明它是乙個左值,否則為右值。這種方法好像是行得通的。

只是,我認為沒有必要分得那麼清楚,又不是在考試。在尋常寫**時,沒有誰會寫類似a+++++a這種考試**。我個人認為。記住最常見的那幾種就差點兒相同了。

比方,字面量(1。『c'這類),暫時(匿名)物件(即將亡值)。經過std::move()轉換的物件,函式返回值。

其它的右值。還是留給編譯器和scott meyers吧。假設真的要細究,能夠參考stackoverflow上的乙個提問《what are rvalues, lvalues, xvalues, glvalues, and prvalues?》

另乙個問題須要說明。const的左值引用(const t&)是乙個萬能引用。既能夠引用左值。也能引用右值。這個是非常特殊,特殊得非常自然。假設test類未定義move建構函式。但使用者又使用test a = std::move(b)構造變數a。那麼終於會呼叫test類的copy建構函式。乙個類的copy建構函式假設使用者不定義。編譯器會在必要情況下自己主動合成乙個。所以上面的a變數肯定能構造。

前一段貌似隱隱約約說到編譯器不會自己主動合成乙個move建構函式。是的。假設使用者定義了copy建構函式。析構函式,operator =中的不論什麼乙個,編譯器都不會自己主動為這個類合成乙個move構成函式以及move 賦值函式。即使須要用到。詳細的規則能夠點這裡。我個人覺得是由於。當定義了那四個函式中的不論什麼乙個,都能夠覺得這個類不是nontrival的了。

想一下,在什麼情況下我們是須要析構函式和copy建構函式的。

當這個類裡面有一些資源(變數)須要我們手動管理的時候。既然有資源要管理。那麼讀者你認為編譯器預設生成的move建構函式的內部實現應該是怎麼樣的呢?對類裡面的全部成員都呼叫std::move進行移動?還是呼叫copy建構函式複製乙份呢?這樣的吃力但又不見得討好的事情,編譯器選擇不幹。畢竟還有前面說到的const t& 能夠引用乙個右值。沒有move建構函式。copy建構函式頂上就可以。

作為類的設計者,你當然知道那些資源(變數)究竟是move還是copy。假設是move的話。那麼直接用=default告訴編譯器:別操心。直接用對全部變數move即可了。例如以下:

class test

~test()=default;

test(const test&)=delete;

test& operator = (const test&)=delete;

test(test &&)=default;//告訴編譯器

test& operator = (test &&)=default;//告訴編譯器

private:

std::unique_ptrp;

}

C 11右值 右值引用以及move語義

1 字面常量 1 3,2 等 2 臨時物件 返回非引用型別的函式,算術 關係 位以及後置遞增 遞減原算符 注意 左值引用的函式,賦值 下標 解引用和前置遞增 遞減運算子返回都為左值 3 無名物件 4 一般函式的返回值也為右值 class myclass myclass 右值 1 右值引用只能繫結到臨...

C 11中的右值引用以及std move

看了很多篇文章,現在終於搞懂了c 中的右值以及std move 左值和右值最重要的區別就是右值其實是乙個臨時的變數 在c 11中,也為右值引用增加了新語法,即 比如如下 void testfunc int i void testfunc int i intmain 此時輸出為 right value...

c 11 右值引用

右值引用 是一種復合型別,跟c 的傳統引用很類似。為更準確地區分兩種型別,我們把傳統的c 引用稱為 左值引用 而使用 引用 這一術語時,我們的意思同時包含兩種引用 左值引用和右值引用。右值引用的行為跟左值引用類似,不同之處在於 右值引用可以繫結到臨時量 右值 而 非const的 左值引用卻不能繫結到...