1 右值引用
A
GetA( ) { return A(); }
GetA() return 右值 temp_obj
pass by value -> return 右值 temp_obj -> copy
`第 1 / 2 行 code`
A a = GetA(); / A&& a = GetA();
右值
temp_obj 賦值 / 被綁定 -> copy / no copy
右值引用 對象:
將 右值
temp_obj lifetime 延續(xù)
到 和 右值引用對象 a 一樣長
2 move 語義
`第 3 行 code`
A(A&& rhs) : ptr(rhs.ptr) ) { rhs.ptr = nullptr; }
1. 解決的問題
class 含 ptr mem
1) shallow copy ctor: only copy ptr
=>
同一 dynamic memory 被 delete > 1 次
, 第 2次 delete 開始,
指針懸掛
2) deep copy: also copy memory
=>
不必要的 heap memory copy
可能 1 份 heap memory 就能實現(xiàn)
3) move
solve 上面 2 個 Prob
2. 3 種 引用
左 / 右 / 常量左 值引用:
只能 / 只能 / 可 綁定 左值 / 右值 / ( const / non-const ) 左 or 右值 obj
, 用 T& / T&& / const T&
綁定右值, 可減少一次 copy
lambda expr 是 rvalue
void AcceptVal(A a) { }
void AcceptRef(const A& a) { }
AcceptVal( GetA() ); // 應調(diào) 2 次 copy ctor
AcceptRef( GetA() ); // 應調(diào) 1 次 copy ctor
`實際上, 兩者 1次 copy ctor 都沒調(diào)`
->
compiler 返回值優(yōu)化
2) 不是所有的 return value 都能被 compiler 優(yōu)化,
但可被 move 語義
優(yōu)化
3. 匹配順序
`右值 temp_obj copy / assignment / move 都有 時,`
`匹配順序:`
(1) 右值引用 > 常量左值引用
`copy/move ctor/assignment + move 都有 時,`
`匹配順序:`
(2) move 語義 > copy 語義
`reason: move/copy 語義 的 function
只 / 可接受 右 / 左 or 右 值參數(shù)`
// shallow / deep copy & move
class A
{
private:
int* ptr; // ptr mem
public:
A() : ptr( new int(0) ) {}
//(1) shallow copy ctor: only copy ptr
A(const A& rhs) : ptr(rhs.ptr) { }
//(2) deep copy ctor : also copy memory
A(const A& rhs) : ptr(new int(*rhs.ptr) ) { }
//(3) move: copy ptr + src ptr set NULL
A(A&& rhs) : ptr(rhs.ptr) ) { rhs.ptr = nullptr; }
~A(){ delete ptr; }
};
A
GetA(){ return A(); }
int main()
{
A a = GetA();
}
// string 的 move 語義
class MyString
{
private:
char* pc;
public:
// copy assignment
MyString&
operator=(const MyString& rhs)
{
if (this == &rhs)
return *this;
delete[] pc;
pc = new char[ strlen(rhs.pc) + 1];
strcpy(pc, rhs.pc);
return *this;
}
// move assignment
MyString&
operator=(MyString&& rhs) noexcept
{
if (this == &rhs)
return *this;
delete[] pc;
pc = rhs.pc;
rhs.pc = nullptr;
return *this;
}
~MyString() { delete[] pc; }
};
3 universal ( 通用 / undefined ) references => 左 or 右 值引用
(1) T&& 與 template 結(jié)合
, 且 自動類型推導 ( func template )
時, T&& 類型不確定 需要推導 => T&& 才是 universal references
(2) universal references: 傳遞 左/右 值, 就是 左/右值引用
完美轉(zhuǎn)發(fā)
正利用該特性
template<typename T>
void f( T&& para){}
//(1) => T = int => + && -> int&&
f(10); // arg 是右值 => T推導成 int => para 是 int&&, 是 右值引用
int x = 10;
//(2) => T = int& => + && -> int&
f(x); //arg 是左值 => T 推導成 int& => para 是 int&&& 折疊成 int&, 是 左值引用
//1
template<typename T>
void f( T&& para); // 調(diào) f 時, T 需推導 => para: universal references
//2
template<typename T>
class A{
A(A&& a); //(1)A type 確定 => para ( a ): 右值引用
};
//3 vector<T> 的 type 必須確定 => T type 必須確定
// => para 無需推導 => para: 右值引用
template<typename T>
void f(std::vector<T>&& para);
4 完美轉(zhuǎn)發(fā)
`第 4 行 代碼`
// 完美轉(zhuǎn)發(fā) std::forward
template <typename T>
void f(T&& val) // val : universal references
{
foo( std::forward<T>(val) );
}
完美轉(zhuǎn)發(fā) ( perfect forward ): func template 的 para 作 arg 傳給 另一 forward func 時, 左/右值特性 不變
函數(shù)調(diào)用 之 形實結(jié)合: 按 arg value 的 左右值屬性 ( lvalue / rvalue ) 匹配 相應 para type
背景
(1) 無 universal reference 時 => value 經(jīng) 2 次 轉(zhuǎn)發(fā) => rvalue 變 lvalue
void f2(int&& para2) {}
void f2(const int& para2) {} //para2:const lvalue ref -> arg = lvalue / rvalue
// arg (val): lvalue -> 應調(diào) f1(int& para1) => T = int -> + & = int&
// -> para1 作 arg: lvalue -> 調(diào) f2(int& para2)
template <typename T>
void f1(T& para1) { f2(para1); }
// arg: (0) rvalue -> 應調(diào) f1(const int& para1) => T = int -> + & = const int&
// -> para1 作 arg: lvalue -> 調(diào) f2(const int& para2)
template <typename T>
void f1(const T& para1) { f2(para1); }
int val = 0;
f1(val); // (1)
f1(0); // (2)
(2) 僅 universal references => 2 次轉(zhuǎn)發(fā)
后, maybe rvalue 變 lvalue
void f2(int&& para2) {}
void f2(int& para2) {]
template <typename T>
void f1(T&& para1) { f2(para1); }
// arg: lvalue -> 應調(diào) f1(int& para1) => T = int&
// -> para1 作 arg: lvalue -> 調(diào) f2(int&)
f1(val);
// arg: rvalue -> 應調(diào) f1(int&& para1) => T = int
// -> para1 作 arg: lvalue -> 調(diào) f2(int&)
f1(0);
(3) universal references + 完美轉(zhuǎn)發(fā)
void f2(int&& para2) { }
void f2(int& para2) { }
// arg (val) lvalue -> 應調(diào) f1(int& para1) => T = int& -> + && = int&
// -> ...para1 作 arg: value = lvalue -> 調(diào) f2(int& para2)
// arg: (val) rvalue -> 應調(diào) f1(int&& para1) => T = int -> + && = int&&
// -> ...para1 作 arg: value = rvalue -> 調(diào) f2(int&& para2)
template <typename T>
void f1(T&& para1)
{
f2( std::forward<T>(para1) ); // 按參數(shù) val 的 value 實際類型 轉(zhuǎn)發(fā)
}
int val = 0;
f1(val);
f1(0);
5 std::move() C++11
`1. 背景`
對象 含 move 語義 的 func
std::string s1 = "hello";
std::string s2;
(1) 右值 obj 作 arg: 隱含調(diào) move=
// ctor + move=: string& operator=(srting&&);
s2 = std::string("world");
(2) 左值 obj 直接作 arg: 匹配調(diào) copy=
s2 = s1; // copy= : string& operator = (const string& );
(3) 想 左值 obj 作 arg : 匹配調(diào) move=
solu:
std::move wrap 左值 obj 為 右值引用 -> 作 arg: 以 匹配調(diào) move=
// string& operator=(const string&&);
s2 = std::move(s1);
=>
解決的問題:
wrap 左值 obj 為 右值引用
-> 作 arg: 以 匹配調(diào)用 obj_class 的 move 語義 func
, 以 move 左值 obj 的 internal handle / heap memory / dynamic array
因為 左值 obj 直接 作 arg, 匹配調(diào)用的是 copy 語義 func
`2. 機制`
將 arg ( 左值/右值 obj ) 強轉(zhuǎn)為 右值引用
3. 具有 move 語義
的 條件
對象 含 handle / heap memory / dynamic array + move 語義 的 func
否則, std::move() 是 `copy 語義`
=>
需 移動語義
的 地方 應該總是用 std::move 將 obj ( 可能是 temp_object ) 轉(zhuǎn)換為 右值引用
-> 沒 move 函數(shù) 時, std::move 默認 copy 操作
void
pop(T& value)
{
// ..
value = std::move( in_stk.top() ); // copy / move 語義
in_stk.pop(); // destory T 型 data_item 本身
}
top: pass by reference
pop: destory