std move 強制轉化為右值

2021-07-14 04:56:33 字數 2345 閱讀 1179

在c++11中,標準庫在中提供了乙個有用的函式std::move,這個函式的名字具有迷惑性。

實際上std::move並不能移動任何東西,它唯一的功能是將乙個左值強制轉化為右值引用,繼而我們可以通過右值引用使用該值,以用於移動語義。

從實現上講,std::move基本等同於乙個型別轉換:

static_cast

(lvalue);

值得一提的是,被轉化的左值,其生命期並沒有隨著左右值的轉化而改變。

如果讀者期望std::move轉化的左值變數lvalue能立即被析構,那麼肯定會失望了。

我們來看**清單3-21所示的例子。

**清單3-21

#include 

using

namespace

std;

class moveable

~moveable()

moveable(const moveable& m)

: i(new

int(*m.i))

moveable(moveable&& m)

: i(m.i)

};int main()

// 編譯選項:g++ -std=c++11 3-3-6.cpp -fno-elide-constructors

在**清單3-21中,我們為型別moveable定義了移動建構函式。

這個函式定義本身沒有什麼問題,但呼叫的時候,使用了moveable c(move(a));這樣的語句。這裡的a本來是乙個左值變數,通過std::move將其轉換為右值。這樣一來,a.i就被c的移動建構函式設定為指標空值。由於a的生命期實際要到main函式結束才結束,那麼隨後對表示式*a.i進行計算的時候,就會發生嚴重的執行時錯誤。

這是個典型誤用std::move的例子。當然,標準庫提供該函式的目的不是為了讓程式設計師搬起石頭砸自己的腳。事實上,要使用該函式,必須是程式設計師清楚需要轉換的時候。比如上例中,程式設計師應該知道被轉化為右值的a不可以再使用。

不過更多地,我們需要轉換成為右值引用的還是乙個確實生命期即將結束的物件。

我們來看看**清單3-22所示的正確例子。

**清單3-22

#include 

using

namespace

std;

class hugemem

~hugemem()

hugemem(hugemem&& hm)

: sz(hm.sz),c(hm.c)

};class moveable

~moveable()

moveable(moveable && m)

:i(m.i), h(move(m.h))

};moveable gettemp()

int main()

// 編譯選項:g++ -std=c++11 3-3-7.cpp -fno-elide-constructors

在**清單3-22中,我們定義了兩個型別:hugemem和moveable,其中moveable包含了乙個hugemem的物件。

在moveable的移動建構函式中,我們就看到了std::move函式的使用。

該函式將m.h強制轉化為右值,以迫使moveable中的h能夠實現移動構造。這裡可以使用std::move,是因為m.h是m的成員,既然m將在表示式結束後被析構,其成員也自然會被析構,因此不存在**清單3-21中的生存期不對的問題。另外乙個問題可能是std::move使用的必要性。這裡如果不使用std::move(m.h)這樣的表示式,而是直接使用m.h這個表示式將會怎樣?

其實這是c++11中有趣的地方:可以接受右值的右值引用本身卻是個左值。

這裡的m.h引用了乙個確定的物件,而且m.h也有名字,可以使用&m.h取到位址,因此是個不折不扣的左值。

不過這個左值確確實實會很快「灰飛煙滅」,因為拷貝建構函式在moveable物件a的構造完成後也就結束了。那麼這裡使用std::move強制其為右值就不會有問題了。而且,如果我們不這麼做,由於m.h是個左值,就會導致呼叫hugemem的拷貝建構函式來構造moveable的成員h(雖然這裡沒有宣告,讀者可以自行新增實驗一下)。如果是這樣,移動語義就沒有能夠成功地向類的成員傳遞。換言之,還是會由於拷貝而導致一定的效能上的損失。

事實上,為了保證移動語義的傳遞,程式設計師在編寫移動建構函式的時候,應該總是記得使用std::move轉換擁有形如堆記憶體、檔案控制代碼等資源的成員為右值,這樣一來,如果成員支援移動構造的話,就可以實現其移動語義。而即使成員沒有移動建構函式,那麼接受常量左值的建構函式版本也會輕鬆地實現拷貝構造,因此也不會引起大的問題。

《深入理解c++11》

std move和右值引用

右值引用允許程式設計人員去避免不必要的記憶體拷貝,從而提高效能。我們知道如果乙個類a的成員變數中有指標,那麼就要考慮深拷貝和淺拷貝了,深拷貝通常要實現下面幾個函式 建構函式 拷貝構造 賦值操作符 這樣做是沒問題的,但是會帶來乙個問題,會造成一些沒必要的拷貝,如 std string str hell...

C 11 右值引用以及std move

int和int 是什麼?都是型別。int是整數型別,int 則是整數引用型別。相同int 也是乙個型別。兩個引號 是c 11提出的乙個新的引用型別。記住,這是乙個新的型別。默念 10次吧。假設你記住這個新型別,那麼非常多疑問都能迎刃而解。而且對 effective modern c 說到的void ...

std move的原理與實現,右值引用的深入理解

這次我真的懂了。首先c 11引入了右值引用 這個要連起來看,是乙個整體,c 多了乙個關鍵字而已。不是引用的引用。是船新的一種語法。那有什麼用呢?額,引數的型別又多了一種!void fun int t void fun int t void fun int t void fun int t 之前的引數...