C++右值引用
左值、右值
右值引用的意义
移动语义
class A{
public:
A(): size(0), data(nullptr){}
A(size_t size): size(size){
data = new int[size];
}
A(const A& rhs){
size = rhs.size;
data = new int[size];
for(int i=0; i
A(A&& rhs){
data = rhs.data;
size = rhs.size;
rhs.size = 0;
rhs.data = nullptr;
cout << "Move constructor" << endl;
}
于是通过std::move将源数据转化为右值后就会调用相应的移动构造函数。
int main(){
A a1 = A(10);
A a2(std::move(a1));
}
完美转发
首先看第一个例子:
#include
using namespace std;
int main(){
int x = 5;
int& left = x;
int&& right = std::move(x);
//cout << &left << endl << &right << endl;
}
在上面的代码中,被声明的左值引用和右值引用都是左值,是可以输出它们的地址的。
然后看二个例子:
#include
using namespace std;
void func(int& x){
cout << "左值" <
在上面的代码中,最后的输出是左值,虽然传给 test 函数的是一个右值,然而在调用 func 时,使用的是 x,参数有了名称,联系第一个例子,此时 x 是一个左值。
如果想要最后的输出是右值,需要使用 std::forward,而 std::forward 中涉及到了引用折叠。
引用折叠
对于T&、T&&来说,其引用类型未定,可能是左值引用,也可能是右值引用,需要视传入的实参而定。
T&
&
T&
左值引用的左值引用折叠为左值引用
T&
&&
T&
右值引用的左值引用折叠为左值引用
T&&
&
T&
左值引用的右值引用折叠为左值引用
T&&
&&
T&&
右值引用的右值引用折叠为右值引用
简而言之,只有两者均为右值引用时,最后的引用类型才为右值引用,否则均为左值引用。
源码分析
remove_reference 源码
template
struct remove_reference
{ typedef _Tp type; };
// 特化版本
template
struct remove_reference<_Tp&>
{ typedef _Tp type; };
template
struct remove_reference<_Tp&&>
{ typedef _Tp type; };
std::forward 源码
template
constexpr _Tp&&
forward(typename std::remove_reference<_Tp>::type& __t) noexcept
{ return static_cast<_Tp&&>(__t); }
根据上述源码,首先通过remove_reference获取 _Tp 的类型 type,然后声明左值引用变量 __t 。
根据 _Tp 的不同,_Tp 会发生引用折叠:
- 当 _Tp 为左值引用时,_Tp折叠为左值引用。
- 当 _Tp 为右值引用时,_Tp折叠为右值引用。
可以发现当 std::forward 的输入是左值引用时,输出也是左值引用;输入是右值引用时,输出也是右值引用。
在下面的代码中,使用了 std::forward 之后就会输出右值。
#include
using namespace std;
void func(int& x){
cout << "左值" <(x));
}
int main(){
test(5);
}
References:
- 一文带你详细介绍c++中的std::move函数
- C++ 理解std::forward完美转发
- 一文读懂C++右值引用和std::move