日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

移动语义(move semantic)和完美转发(perfect forward)

發布時間:2023/12/31 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 移动语义(move semantic)和完美转发(perfect forward) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

完整原文鏈接:https://codinfox.github.io/dev/2014/06/03/move-semantic-perfect-forward/

移動語義(move semantic)

通過移動語義,我們可以在沒有必要的時候避免復制。那么在接下來,我們就重點來談一談移動構造函數(move constructor)。相信到這里你已經意識到了,移動構造函數的出現就是為了解決復制構造函數的這個弊病。所以,其實移動構造函數應該和復制構造函數實現差不多的功能。那么,它也應該是一種構造函數的重載(好廢的廢話……)。所以,我們可以想象出來,其實移動構造函數大概就會是這個樣子:

Test(<KEYWORD> t):arr(t.arr){t.arr = nullptr;}

這里解釋一下,通過移動構造函數,事實上我們是做了一個淺拷貝(shallow copy)。至于要將之前的指針置為空的原因在于,我們的類會在析構的時候delete掉我們的數組。那么我們淺拷貝出來的這個對象的成員變量(arr指針)就變成了一個懸掛指針(dangling pointer)。

好了,現在的問題變成了,這個<KEYWORD>究竟是什么?編譯器如何自動判斷到底應該調用復制構造函數(我突然想起來這個東西的翻譯貌似應該是拷貝構造函數,但是既然都已經寫了這么多了,我就不改了)還是移動構造函數呢?

....................

進一步探討左值和右值

我們來考慮下面的情景:

void doWork(TYPE&& param) {// ops and expressions using std::move(param) }

這個代碼是從Scott Meyers的演講當中摘取的。現在的問題是:** param是右值嗎? **答案是:不!param是一個左值。

這里牽扯到一個概念,即事實上左值和右值與類型是沒有關系的,即int既可以是左值,也可以是右值。區別左值和右值的唯一方法就是其定義,即能否取到地址。在這里,我們明顯可以對param進行取地址操作,所以它是一個左值。也就是說,但凡有名字的“右值”,其實都是左值。這也就是為什么上面的代碼當中鼓勵大家對所有的變量使用std::move()轉成右值的原因。

....................

完美轉發(perfect forward)又是在做什么

我們依然考慮一個例子:

template <typename T> void func(T t) {cout << "in func" << endl; }template <typename T> void relay(T&& t) {cout << "in relay" << endl;func(t); }int main() {relay(Test()); }

在這個例子當中,我們的期待是,我們在main當中調用relay,Test的臨時對象作為一個右值傳入relay,在relay當中又被轉發給了func,那這時候轉發給func的參數t也應當是一個右值。也就是說,我們希望:當relay的參數是右值的時候,func的參數也是右值;當relay的參數是左值的時候,func的參數也是左值

那么現在我們來運行一下這個程序,我們會看到,結果與我們預想的似乎并不相同:

default constructor in relay copy constructor in func destructor destructor

我們看到,在relay當中轉發的時候,調用了復制構造函數,也就是說編譯器認為這個參數t并不是一個右值,而是左值。這個的原因已經在上一節將結果了,因為它有一個名字。那么如果我們想要實現我們所說的,如果傳進來的參數是一個左值,則將它作為左值轉發給下一個函數;如果它是右值,則將其作為右值轉發給下一個函數,我們應該怎么做呢?

這時,我們需要std::forward<T>()。與std::move()相區別的是,move()會無條件的將一個參數轉換成右值,而forward()則會保留參數的左右值類型。所以我們的代碼應該是這樣:

template <typename T> void func(T t) {cout << "in func " << endl; }template <typename T> void relay(T&& t) {cout << "in relay " << endl;func(std::forward<T>(t)); }

現在運行的結果就成為了:

default constructor in relay move constructor in func destructor destructor

而如果我們的調用方法變成:

int main() {Test t;relay(t); }

那么輸出就會變成:

default constructor in relay copy constructor in func destructor destructor

完美地實現了我們所要的轉發效果。

.............

................

后記

C++0x通過引入許多新的語言特性來實現了語言性能的提升,使得本來就博大精深的一門語言變得更加的難以學習。但是一旦了解,就會被語言精妙的設計所折服。參考資料中給出了更多的關于左值、右值、左值引用、右值引用、移動語義和完美轉發的例子。我自己實在是沒有精力看完所有的這些資料了,各位有興趣的話可以參閱。

參考資料

  • http://thbecker.net/articles/rvalue_references/section_01.html#section_01
  • http://blog.csdn.net/pongba/article/details/1697636
  • http://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Scott-Meyers-Universal-References-in-Cpp11
  • https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers
  • https://onedrive.live.com/view.aspx?resid=F1B8FF18A2AEC5C5!1062
  • ?

    創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

    總結

    以上是生活随笔為你收集整理的移动语义(move semantic)和完美转发(perfect forward)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。