移动语义(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通過引入許多新的語言特性來實現了語言性能的提升,使得本來就博大精深的一門語言變得更加的難以學習。但是一旦了解,就會被語言精妙的設計所折服。參考資料中給出了更多的關于左值、右值、左值引用、右值引用、移動語義和完美轉發的例子。我自己實在是沒有精力看完所有的這些資料了,各位有興趣的話可以參閱。
參考資料
?
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的移动语义(move semantic)和完美转发(perfect forward)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: gta5卖车的地方在哪
- 下一篇: 和平精英情侣模式在哪里