C++03 中臨時對象的拷貝構造與析構帶來顯著性能開銷。雖然編譯器提供 Copy Elision、RVO/NRVO 優化,但並非所有場景都能優化(如 swap 函數中必須構造臨時對象)。C++11 引入右值引用,賦予臨時對象「移動語義」,以淺拷貝代價實現資源轉移,徹底解決此類效率問題。
A a = 42; // 理論:臨時對象 → 拷貝構造 a → 析構臨時對象
// 優化後:直接用 42 構造 a,零拷貝
A get() { return A(1); }
A a = get(); // 優化掉 tmp1、tmp2 兩個臨時對象
A get() { A tmp(1); return tmp; } // tmp 與外部 a 共用內存地址
局限:上述優化屬於編譯器「允許但不強制」行為,且無法處理 swap 等必須構造臨時對象的場景。
C++11 引入右值引用後,表達式分類細化為 glvalue / prvalue / xvalue,但初學者只需掌握「能取址→左值,否則→右值」即可。
| 引用類型 | 可綁定對象 |
|---|---|
非 const 左值引用 (T&) | 非 const 左值 |
const 左值引用 (const T&) | 所有左值、所有右值 |
非 const 右值引用 (T&&) | 非 const 右值 |
const 右值引用 (const T&&) | 所有右值 |
函數類型例外:函數名可綁定到任意類型引用(void(&)()、void(&&)() 均合法)。
// 簡易 move 實現(C++11 標準庫內部原理)
template
typename remove_reference::type&& move(T&& t) {
return static_cast::type&&>(t);
}
class A {
char* m_data;
public:
A(const char* s) { /* 深拷貝構造 */ }
A(const A& a) { /* 深拷貝構造 */ PRINT("copy ctor"); }
A(A&& a) : m_data(a.m_data) { a.m_data = nullptr; PRINT("move ctor"); } // 掠奪資源
A& operator=(A&& a) { /* move assign */ }
~A() { delete[] m_data; }
};
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); // 輸出:move ctor, move assign, move assign, 3×dtor
}
輸出證明:全程無深拷貝,僅指針轉移,效率大幅提升。
noexcept,助推標準庫容器優化std::move 將左值轉為右值引用,明確表達「資源轉移」意圖std::move(已是右值)