移動語意是多載選擇與型別定義的狀態轉移,不是自動的擁有權轉移。請先從運算式的值類別開始,再選擇能描述擁有權與生命週期的參數型別。
運算式類別 #
每個非 void 運算式恰好屬於下列其中一種類別:
- 左值識別物件、函式或位元欄位。
- 將亡值識別其資源可以重複使用的物件。
- 純右值計算值或初始化物件。
左值與將亡值是廣義左值;純右值與將亡值是右值。具名變數即使型別為 T&&,仍是左值運算式。
int& counter();
int&& cache_slot();
counter() = 42; // The call is an lvalue.
int&& slot = cache_slot();
consume(slot); // `slot` is an lvalue expression.
consume(std::move(slot)); // Casts it to an xvalue.
回傳 T 的呼叫是純右值,回傳 T& 的呼叫是左值,回傳 T&& 的呼叫是將亡值。std::move 執行將亡值轉型;它不會移動資料,也不會結束來源物件的生命週期。
移動建構是多載選擇 #
參照繫結提供相關區別:
T&繫結相容的左值。const T&繫結左值、將亡值與純右值。T&&繫結將亡值與純右值。
std::string source = "payload";
std::string destination = std::move(source); // Selects a move constructor when viable.
選取的建構函式定義轉移方式。它可能轉移配置、複製小型表示,或產生與複製無法區別的效果。依值回傳區域變數時通常應使用 return local;:複製省略可能直接建構結果,而不適用省略時,符合資格的區域變數可以在多載選擇中被視為將亡值。return std::move(local); 可能抑制具名回傳值最佳化。
轉送參照保留呼叫端的類別 #
推導樣板參數中的 T&& 是轉送參照。對左值引數,T 推導為 U&;對右值,T 推導為 U。參照摺疊會將 U& && 簡化為 U&,因此 std::forward<T> 可以重現原始類別。
template<class T>
void relay(T&& value) {
consume(std::forward<T>(value));
}std::move(value) 會將兩種情況都轉為將亡值,可能意外從左值呼叫端移動。未使用推導的 T&&(例如類別樣板中 T 已固定的成員)是一般右值參照,而不是轉送參照。
參數設計從擁有權開始 #
使用描述被呼叫端契約的參數形式:
| 形式 | 契約 |
|---|---|
const T& | 在呼叫期間以唯讀方式借用。 |
T& | 在呼叫期間借用並變更。 |
T | 取得獨立值;呼叫端複製或移動至其中。 |
T&& | 要求可由被呼叫端消費的右值;不蘊含擁有權。 |
std::unique_ptr<T> | 轉移獨佔擁有權。 |
std::shared_ptr<T> | 被呼叫端保留一份擁有權時共用擁有權。 |
T* | 借用,API 明確表達可為空性。 |
儲存獨立值的建構函式可以依值取得參數,並從參數移動:
class Message {
public:
explicit Message(std::string text) : text_{std::move(text)} {}
private:
std::string text_;
};左值呼叫端會將值複製至參數;右值呼叫端可以移動至其中。查詢或僅借用物件時,不適合依值傳遞。
不要使用 FILE*&& 或其他原始指標右值參照來表示擁有權。移動原始指標只會複製其位址;兩個名稱仍指向相同資源。呼叫會轉移銷毀責任時,請使用具擁有權的包裝器,例如 std::unique_ptr<T, Deleter>。只使用智慧指標所擁有物件的函式,應依存取權與可為空性接受 T&、const T&、T* 或 const T*;不需要接受智慧指標控制代碼。
被移動來源的物件仍然存在 #
標準函式庫物件被移動後,除非規格另有說明,會處於有效但未指定的值。銷毀、指定,以及前置條件接受該狀態的作業都是有效的。不要假設被移動的 std::vector 為空或保留容量。
std::vector<int> buffer = read_row();
rows.push_back(std::move(buffer));
buffer.clear(); // Valid, but capacity reuse is not guaranteed.
借用不得超過擁有者的生命週期。移動擁有 unique_ptr 不會銷毀指向物件,也不會銷毀所有既有借用,但之後銷毀或重設新的擁有者可能使那些借用懸空。移動作業解決表示轉移;它不會消除陳述生命週期的需求。
評論
在 GitHub 上閱讀或評論(發表評論需要登入)