左值引用和右值引用
C++11 朝码夕解: move 和 forward
# 左值和右值
- 左值 (
lvalue
):非临时变量。在内存有确定存储地址、有变量名,表达式结束依然存在的值。int a=10; //a 为非常量左值(有确定存储地址,也有变量名)
const int a1=10; //a1 为常量左值(有确定存储地址,也有变量名)
const int a2=20; //a2 为常量左值(有确定存储地址,也有变量名)
- 右值 (
rvalue
):临时变量。在内存没有确定存储地址、没有变量名,表达式结束就会销毁的值。int a=10; // 10 为非常量右值
const int a1=10;
const int a2=20;
a1+a2 // (a1+a2) 为常量右值
左值引用和右值引用是指左值或者右值的型别,左值的型别可以为左值引用,也可以为右值引用。右值的型别可以为右值引用,但不能为左值引用,因为左值引用仅能引用左值 ( const
修饰的左值引用可以引用右值, 因为函数参数重 const
修饰的参数,编译器会为传入的右值创建临时变量,所以 cosnt 修饰的左值引用其实是引用的这个临时变量)。右值引用仅能引用纯右值。
# std::move 和 std::forward
- std::move
std::move
强制转换为右值。推导后的 param 的类型为 T&(实参为左值时) 或 T (实参为右值时)。返回时将 param 去除引用后强制转换为 T&& 型别,最终的返回结果为 T&& 型别的右值。template <class T>
typename remove_reference<T>::type&& move(T&& param){
typedef typename remove_reference<T>::type _Up;
return static_cast<_Up&&>(param);
}
std::move转
换为右值时,若保留了常量性const
,则不会调用移动构造函数,而是调用复制构造函数。
- std::forward
std::forward
使用时需要具化:std::forward<T>();
template<typename T1, typename T2> | |
void set(T1 && var1, T2 && var2){ | |
m_var1 = std::forward<T1>(var1); | |
m_var2 = std::forward<T2>(var2); | |
} | |
//when var1 is an rvalue, std::forward<T1> equals to static_cast<[const] T1 &&>(var1) | |
//when var1 is an lvalue, std::forward<T1> equals to static_cast<[const] T1 &>(var1) |
如果外面传来了 rvalue
临时变量,它就转发 rvalue
并且启用 move
语义.
如果外面传来了 lvalue
, 它就转发 lvalue
并且启用复制。然后它也还能保留 const
。
# 区分右值引用和万能引用
万能引用的条件:
T&&
T
的类型由推导而来
template <typename T> | |
void f(const T&& param); //param 是个右值引用 |