C++ 歷史上臨時對象產生與拷貝效率低下。編譯器優化(Copy Elision、RVO/NRVO)能消除部分情況,但無法覆蓋如 `swap` 等必須使用臨時變數的場景。C++11 引入右值引用,賦予臨時對象「移動語意」,以淺拷貝代價實現資源轉移。
表達式非左即右。判斷:能取地址(`&expr`)為左值,否則為右值。字面值、臨時對象為右值;具名變數為左值。
| 引用類型 | 可綁定對象 |
|---|---|
| 非 const 左值引用 (`T&`) | 非 const 左值 |
| const 左值引用 (`const T&`) | 所有左值、右值 |
| 非 const 右值引用 (`T&&`) | 非 const 右值 |
| const 右值引用 (`const T&&`) | 所有右值 |
函數類型例外:右值引用可綁定函數左值。
具名右值引用為 lvalue,不具名為 xvalue。
為類別添加移動建構函數與移動指派運算子:
class A {
char* m_data;
public:
A(const char* s) { m_data = s ? strcpy(new char[strlen(s)+1], s) : 0; }
// 拷貝
A(const A& a) { m_data = a.m_data ? strcpy(new char[strlen(a.m_data)+1], a.m_data) : 0; }
A& operator=(const A& a) { if(this!=&a){ delete[] m_data; m_data = a.m_data ? strcpy(new char[strlen(a.m_data)+1], a.m_data) : 0; } return *this; }
// 移動
A(A&& a) : m_data(a.m_data) { a.m_data = 0; }
A& operator=(A&& a) { if(this!=&a){ m_data = a.m_data; a.m_data = 0; } return *this; }
~A() { delete[] m_data; }
};
移動操作僅轉移指標所有權,置空來源,避免深拷貝 `strcpy`。
template
typename remove_reference::type&& move(T&& t) {
return static_cast::type&&>(t);
}
利用模板參數推導:實參為左值 `T&` 時,`T` 推導為 `U&`,參數類型變為 `U& &&` 崩潰為 `U&`,返回 `U&&`,達到「左值視為右值」效果。`remove_reference` 剝離引用層。
void swap(A& a, A& b) {
A tmp(move(a));
a = move(b);
b = move(tmp);
}
int main() {
A a("123"), b("456");
swap(a, b);
return 0;
}
輸出:
constructor constructor move constructor move assignment move assignment destructor destructor destructor
全程無拷貝建構/指派,移動語意生效。