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 延续
到 和 右值引用对象 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 就能实现
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() ); // 应调 2 次 copy ctor
AcceptRef( GetA() ); // 应调 1 次 copy ctor
`实际上, 两者 1次 copy ctor 都没调`
->
compiler 返回值优化
2) 不是所有的 return value 都能被 compiler 优化,
但可被 move 语义
优化
3. 匹配顺序
`右值 temp_obj copy / assignment / move 都有 时,`
`匹配顺序:`
(1) 右值引用 > 常量左值引用
`copy/move ctor/assignment + move 都有 时,`
`匹配顺序:`
(2) move 语义 > copy 语义
`reason: move/copy 语义 的 function
只 / 可接受 右 / 左 or 右 值参数`
// 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 结合
, 且 自动类型推导 ( func template )
时, T&& 类型不确定 需要推导 => T&& 才是 universal references
(2) universal references: 传递 左/右 值, 就是 左/右值引用
完美转发
正利用该特性
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); // 调 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 完美转发
`第 4 行 代码`
// 完美转发 std::forward
template <typename T>
void f(T&& val) // val : universal references
{
foo( std::forward<T>(val) );
}
完美转发 ( perfect forward ): func template 的 para 作 arg 传给 另一 forward func 时, 左/右值特性 不变
函数调用 之 形实结合: 按 arg value 的 左右值属性 ( lvalue / rvalue ) 匹配 相应 para type
背景
(1) 无 universal reference 时 => value 经 2 次 转发 => rvalue 变 lvalue
void f2(int&& para2) {}
void f2(const int& para2) {} //para2:const lvalue ref -> arg = lvalue / rvalue
// arg (val): lvalue -> 应调 f1(int& para1) => T = int -> + & = int&
// -> para1 作 arg: lvalue -> 调 f2(int& para2)
template <typename T>
void f1(T& para1) { f2(para1); }
// arg: (0) rvalue -> 应调 f1(const int& para1) => T = int -> + & = const int&
// -> para1 作 arg: lvalue -> 调 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 次转发
后, maybe rvalue 变 lvalue
void f2(int&& para2) {}
void f2(int& para2) {]
template <typename T>
void f1(T&& para1) { f2(para1); }
// arg: lvalue -> 应调 f1(int& para1) => T = int&
// -> para1 作 arg: lvalue -> 调 f2(int&)
f1(val);
// arg: rvalue -> 应调 f1(int&& para1) => T = int
// -> para1 作 arg: lvalue -> 调 f2(int&)
f1(0);
(3) universal references + 完美转发
void f2(int&& para2) { }
void f2(int& para2) { }
// arg (val) lvalue -> 应调 f1(int& para1) => T = int& -> + && = int&
// -> ...para1 作 arg: value = lvalue -> 调 f2(int& para2)
// arg: (val) rvalue -> 应调 f1(int&& para1) => T = int -> + && = int&&
// -> ...para1 作 arg: value = rvalue -> 调 f2(int&& para2)
template <typename T>
void f1(T&& para1)
{
f2( std::forward<T>(para1) ); // 按参数 val 的 value 实际类型 转发
}
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: 隐含调 move=
// ctor + move=: string& operator=(srting&&);
s2 = std::string("world");
(2) 左值 obj 直接作 arg: 匹配调 copy=
s2 = s1; // copy= : string& operator = (const string& );
(3) 想 左值 obj 作 arg : 匹配调 move=
solu:
std::move wrap 左值 obj 为 右值引用 -> 作 arg: 以 匹配调 move=
// string& operator=(const string&&);
s2 = std::move(s1);
=>
解决的问题:
wrap 左值 obj 为 右值引用
-> 作 arg: 以 匹配调用 obj_class 的 move 语义 func
, 以 move 左值 obj 的 internal handle / heap memory / dynamic array
因为 左值 obj 直接 作 arg, 匹配调用的是 copy 语义 func
`2. 机制`
将 arg ( 左值/右值 obj ) 强转为 右值引用
3. 具有 move 语义
的 条件
对象 含 handle / heap memory / dynamic array + move 语义 的 func
否则, std::move() 是 `copy 语义`
=>
需 移动语义
的 地方 应该总是用 std::move 将 obj ( 可能是 temp_object ) 转换为 右值引用
-> 没 move 函数 时, 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