移動語意與右值參考(move semantics and rvalue references)
假設你搬家,要把一個箱子的東西倒進另一個。複製語意說:再買一整套一模一樣的東西塞進新箱子(慢,而且你現在每樣都有兩份)。移動語意說:直接把舊箱子裡的東西拿起來放進新箱子,讓舊箱子空著(快,什麼都沒複製)。當舊箱子本來就要丟掉時,用移動取代複製是純粹的賺到——而這就是整個想法。
使這成為可能的機制是右值參考,寫成兩個 & 號,像 Widget&&。普通的參考 Widget& 繫結到一個你之後可能還會用的具名物件(左值,lvalue)。右值參考 Widget&& 繫結到暫存物件,或被明確標記為可拋棄的東西——右值(rvalue)——它示意「這個物件即將消亡,所以你可以安全地偷走它的內臟」。移動建構子或移動賦值接受一個 Widget&&,不做深複製,而是把來源的指標與大小掠奪過來,然後把來源留在有效但空的狀態(這樣它的解構子仍會無害地執行)。對 std::vector 來說,移動只是轉移內部緩衝區指標——常數時間,不複製任何元素。
為何重要:從函式回傳一個大 vector、讓容器成長、或傳遞所有權,過去都意味著昂貴的複製;移動語意讓這些操作變得廉價,常常只是幾個指標賦值。關鍵的誠實要點:從一個物件移動並不會銷毀它——它被留在「有效但未指定」的狀態,所以你可以安全地銷毀或重新賦值它,但不該假設它現在裝著什麼。而且移動只有在型別真的實作了移動時才比複製便宜;對一個全是 int 的純 struct,「移動」就只是複製。
std::vector<int> v = make_big_vector(); std::vector<int> w = std::move(v); // w 偷走 v 的緩衝區;v 現在有效但空,沒有複製任何元素
移動 vector 是常數時間的指標轉移;複製它則會碰到每一個元素。
常見誤解:std::move 不會移動任何東西——它只是個轉型,把引數標記為右值,好讓移動操作得以被選用。若不存在移動建構子,被「移動」的物件會悄悄改用複製,你照樣付出全額代價且毫無警告。