C++11--引言折叠与完美转发

目录

一,引言

二,引用折叠

案例一:

案例二:

三,完美转发


一,引言

在C++11引入右值引用之后,就存在引用的引用。如下:

cpp 复制代码
template<class T>
void f1(T& x)
{}

T的类型就有很多种,如T可以实例化为原本类型,也可以T&,或者T&&。为此引入引用折叠的概念。

二,引用折叠

在进行模板或typedef时会构成引用的引用。首先需要先介绍引用折叠的规则:

cpp 复制代码
typedef int& lref;
typedef int&& rref;
int n = 0;
lref& r1 = n; 
lref&& r2 = n; 
rref& r3 = n;  
rref&& r4 = 1; 

首先lref的类型是int&。rref的类型是int&&。n的类型是int。

r1的类型是int& ----------------int& 和 &进行引用折叠折叠成int&

r2的类型是int& -----------------int& 和 &&进行引用折叠折叠成int&

r3的类型是int& -----------------int&& 和 & 进行引用折叠折叠成int&

r3的类型是int&& ----------------int&& 和 && 进行引用折叠折叠成int&&

由此可以发现只有右值引用和右值引用进行引用折叠才可以折叠成右值引用,其余情况经过引用折叠全都折叠成左值引用。下面来举两个例子加深以下理解:

案例一:

cpp 复制代码
template<class T>
void f1(T& x)
{}

第一种情况:

cpp 复制代码
int n = 0;
f1<int>(n);
f1<int>(0);

T通过显示实例化为int。f1为左值引用,n为左值。0为右值。所以第一个可以运行,第二个报错,左值不能传右值。

第二种情况:

cpp 复制代码
f1<int&>(n);
f1<int&>(0);

T通过显示实例化为int&。经过引用折叠折叠为void f1(int& x) 。n为左值,0为右值。第二个报错。

第三种情况:

cpp 复制代码
f1<int&&>(n);
f1<int&&>(0);

T通过显示实例化为int&&。经过引用折叠为void f1(int& x) 。n为左值,0为右值。第二个报错。

第四种情况:

cpp 复制代码
f1<const int&>(n);
f1<const int&>(0);

T通过显示实例化为const int& 。经过引用折叠为void f1(const int& x) 。const左值可以接收右值。上述两种情况都可以通过。

第五种情况:

cpp 复制代码
f1<const int&&>(n);
f1<const int&&>(0);

T通过显示实例化为const int&。经过引用折叠为void f1(const int& x) 。const左值可以接收右值。


下面来看当函数的参数是右值的情况:

cpp 复制代码
template<class T>
void f2(T&& x)
{}

第一种情况:

cpp 复制代码
f2<int&>(n);
f2<int&>(0);

T实例化为int&。经过引用折叠为void f2(int& x)。因此运行结果和上述第二种是一致的。

第二种情况:

cpp 复制代码
f2<int&&>(n); 
 
f2<int&&>(0);

T实例化为int&&。经过引用折叠为void f2(int&& x) 。因此当传入左值会报错。只能传入右值。

上述都是传参时经过显示实例化。下面来举不显示实例化,由编译器自行进行推到的情况。

案例二:

cpp 复制代码
template<class T>
void Function(T&& t)
{
int a = 0;
T x = a;
//x++;
cout << &a << endl;
cout << &x << endl << endl;
}

第一种情况:

cpp 复制代码
Function(10); 

10为右值函数识别T的类型为int。函数实例化为void Function(int&& t)。

第二种情况:

cpp 复制代码
int a;
Function(a); 

a为左值函数识别T的类型为int&。经过引用折叠,函数实例化为void Function(int& t)。


总结 :右值引用和左值引用进行引用折叠时,总会折叠为左值引用。在上述案例二中,参数为右值引用,由于引用折叠的原因不管参数传入左值还是右值都可以通过引用折叠的功能进行解决。当传入左值时,就会折叠为该类型的引用。当传入右值时,不进行折叠。因此案例二的参数引用模式称为万能引用

三,完美转发

在上一章中讲过表达式本身是左值属性。也就意味着不管是左值引用还是右值引用。参数变量的属性都是左值属性。为此C++为了保留该变量的原本属性引入了完美转发的概念。例如:

上一节中当传入右值时,函数实例化为void Function(int&& t)。虽然是右值引用,但是t的属性为左值。为了使得t的属性依旧保持为右值属性。需要下面关键字:

举个例子:

cpp 复制代码
template <class T>
void fun(T&& n)
{

}
template<class T>
void f1(T&& x)
{
	fun(std::forward<T>(x));
}
int main()
{
	f1(0);
	return 0;
}

如果不进行forward修饰x的属性为左值。通过fun函数引用折叠为void fun(int& n);若经过forward修饰之后。x依旧为右值属性。则fun函数不需要引用折叠实例化为fun (int&& n)。

相关推荐
仰泳的熊猫几秒前
题目 1429: 蓝桥杯2014年第五届真题-兰顿蚂蚁
数据结构·c++·算法·蓝桥杯
Yupureki9 分钟前
《算法竞赛从入门到国奖》算法基础:入门篇-分治
c语言·开发语言·数据结构·c++·算法·贪心算法
无心水12 分钟前
4、Go语言程序实体详解:变量声明与常量应用【初学者指南】
java·服务器·开发语言·人工智能·python·golang·go
ZPC821012 分钟前
psutil
开发语言·php
jiunian_cn13 分钟前
【C++】线程库
开发语言·c++
0x5314 分钟前
JAVA|智能仿真并发项目-并行与并发
java·开发语言
漫漫求16 分钟前
1、IM:基础连接
开发语言·后端·golang
gjxDaniel24 分钟前
JavaScript编程语言入门与常见问题
开发语言·javascript
kk哥889925 分钟前
C++新手入门
开发语言·c++
Hello eveybody26 分钟前
Java发明者介绍
java·开发语言