C++11 左值引用和右值引用与引用折叠和完美转发

1.左值与右值

最感性的认识。

当然,左值也是可以在右边的。

左值是可以被修改的,右值不能。

当然取地址也是。

生存周期一般左值会比右值的长,一般右值都计算时产生的无名临时对象,存在时间比较短。

下面还有一种情况也要区分。

2.左值引用和右值引用

左值引用:可以引用一个对象,有时候也可以绑定一个右值。

右值引用:只能引用右值。

1左值引用示例

看以下代码,比较正常。

	int a = 3;
	int &p1 = a;  // 左值引用

若左值引用右值将报错

但加上const就可以引用了

2右值引用示例

不能把左值绑定到右值,但使用move可以把左值转换右值就可以绑定

示例1

示例2

示例3

理解了上边知识,接下来是引用折叠规则,

3.引用折叠

先看演示代码

#include <iostream>
using namespace std;

using lRef = int&;   //左值引用
using rRef = int&&;	 //右值引用

int main(int argc, char **argv)
{
	is_lvalue_reference<lRef &>::value ?
		cout << "lRef &  左值引用" << endl :
		cout << "lRef &  右值引用" << endl;

	is_lvalue_reference<lRef &&>::value ?
		cout << "lRef && 左值引用" << endl :
		cout << "lRef && 右值引用" << endl;


	is_rvalue_reference<rRef &>::value ?
		cout << "rRef &  右值引用" << endl :
		cout << "rRef &  左值引用" << endl;

	is_rvalue_reference<rRef &&>::value ?
		cout << "rRef && 右值引用" << endl :
		cout << "rRef && 左值引用" << endl;


	return 0;
}

调试结果

这就是引用折叠规则。

这怎么理解呢,看下图

可以看到只有都是右值引用的时候才是右值引用,当然只有一个右值引用的情况下自然而然也是右值引用。

这是就引用折叠。

4.完美转发

主要用于参数转发时是左值传入还是右转入
考虑以下代码

#include <iostream>
using namespace std;

template<typename T> 
void Fun1(T& v)
{
	cout << "左值引用调用" << v << endl;
}

template<typename T>
void Fun1(T&& v)
{
	cout << "右值引用调用" << v <<endl;
}

template<typename T>
void Fun(T&& v)
{
	Fun1(v);
}

int main(int argc, char **argv)
{
	int a = 3;

	Fun(a);

	return 0;
}

主函数里给Fun传入a,根据上边知识,a是一个左值,看调试结果是调用哪一个重载版本Fun1

结果跟预想的一样,接下来更改换右值传入。

int main(int argc, char **argv)
{

	Fun(5);

	return 0;
}

调试结果

我们发现跟想象中不一样!!!

这时候该怎么办。

c++11中提供了一个用于完美转发的函数forward。

还提供了一个move函数,用于把左值变成右值的方法。

forward会根据引用折叠规则得出传入的是左值引用还是右值引用

接下来只需更改一下Fun函数,其他的不变

template<typename T>
void Fun(T&& v)
{
	Fun1(forward<T>(v));
}

调试结果

发现跟我们预想一样了。

完美转发完整示例

#include <iostream>
using namespace std;

template<typename T> 
void Fun1(T& v)
{
	cout << "左值引用调用" << v << endl;
}

template<typename T>
void Fun1(T&& v)
{
	cout << "右值引用调用" << v <<endl;
}

template<typename T>
void Fun(T&& v)
{
	Fun1(forward<T>(v));
}

int main(int argc, char **argv)
{
	int a = 1;
	Fun(a);
	Fun(move(a));

	const int b = 2;
	Fun(b);
	Fun(move(b));

	Fun(5);

	return 0;
}

调试结果

5.结语

学无止境。
—End

Tags: