c++ 右值引用&std::move()&移動建構函式&移動賦值函式
阿新 • • 發佈:2020-08-01
右值引用
所謂右值引用就是必須繫結到右值的引用,我們通過 && 而不是 & 來獲得右值引用。
右值引用有一個很重要的性質--只能繫結到一個將要銷燬的物件,即左值持久,右值短暫。
int i = 42; int& r = i; // 正確,r 引用 i int&& rr = i; // 錯誤,不能將一個右值引用繫結到一個左值上 int& r2 = i * 42; // 錯誤,i * 42 是一個右值 const int& r3 = i * 42; // 正確,可以將一個 const 的引用繫結到一個右值上 int&& rr2 = i * 42; // 正確,將 rr2 繫結到乘法結果上
標準庫 move 函式
雖然不能將一個右值引用直接繫結到一個左值上,但我們可以顯式地將一個左值轉換為對應的右值引用型別。我們還可以通過呼叫一個名為 move 的新標準函式來獲得繫結到左值的右值引用。
int&& rr3 = std::move(rr1); // ok
move 呼叫告訴編譯器:我們有一個左值,但我們希望像一個右值一樣處理它,我們必須認識到,呼叫 move 就意味著:除了對 rr1 賦值或銷燬它以外,我們將不再使用它,在呼叫 move 之後, 我們不能對移後源物件的值做任何假設。
移動建構函式&移動賦值運算子
移動建構函式和移動賦值運算子“竊取”資源,它們通常不分配任何資源。因此這兩類函式通常不會丟擲任何異常,當不丟擲異常時必須宣告為 noexcept。
Foo(Foo&& v) noexcept { // 移動建構函式 ; // TODO } Foo& operator = (Foo&& a) noexcept { // 移動操作運算子 ; // TODO return *this; }
用拷貝建構函式代替移動建構函式幾乎肯定是安全的,用賦值運算子代替移動賦值運算子也幾乎肯定是安全的。
Foo x; Foo y(x);// 拷貝建構函式,x是一個左值 Foo z(std::move(x)); // 拷貝建構函式,因為未定義移動建構函式
在對 z 進行初始化時,我們呼叫了 move(x),它返回一個繫結到 x 的 Foo&&。Foo 拷貝建構函式是可行的,因為我們可以將一個 Foo&& 轉換為一個 const Foo&。因此 z 的初始化將使用 Foo 的拷貝建構函式。
引用限定符
用來指出一個非 static 成員函式可以用於左值或右值的符號。限定符 & 和 && 應該放在引數列表之後或 const 之後(如果有的話)。被 & 限定的函式只能用於左值,被 && 限定的函式只能用於右值。
Foo x; Foo y(x); // 拷貝建構函式,x是一個左值 Foo z(std::move(x)); // 拷貝建構函式,因為未定義移動建構函式
程式碼示例
class A { public: int val = 0; A() { std::cout << "A()" << std::endl; } A(int v) { std::cout << "A(int v)" << std::endl; } A& operator = (const A& a) { std::cout << "A& operator = (const A& a)" << std::endl; return *this; } A(const A& a) { std::cout << "A(const A& a)" << std::endl; } A& operator = (A&& a) { std::cout << "A& operator = (A&& a)" << std::endl; return *this; } A(A&& v) { std::cout << "A(A&& v)" << std::endl; } virtual ~A() { std::cout << "~A()" << std::endl; } void print(int a) && { // 如果只有此函式,c.print(3);編譯報錯 std::cout << "print(int a) &&" << std::endl; } void print(int a) & { // 如果只有此函式,std::move(d).print(2);編譯報錯 std::cout << "print(int a) &" << std::endl; } }; int main() { A b; // A() A c; // A() A d; // A() c = b; // A& operator = (const A& a) d = std::move(b); // A& operator = (A&& a) std::move(d).print(2); // print(int a) && c.print(3); // print(int a) & // ~A() // ~A() // ~A() return 0; }