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)。

相关推荐
码农三叔6 小时前
(4-2-05)Python SDK仓库:MCP服务器端(5)Streamable HTTP传输+Streamable HTTP传输
开发语言·python·http·大模型·1024程序员节·mcp·mcp sdk
十铭忘6 小时前
Vue3实现Pixso中的钢笔工具
开发语言·javascript·vue
cpp_25016 小时前
P8597 [蓝桥杯 2013 省 B] 翻硬币
数据结构·c++·算法·蓝桥杯·题解
IT枫斗者6 小时前
Spring Boot 4.0 正式发布:新一代起点到底“新”在哪?(Spring Framework 7 / Java 25 / JSpecify / API 版本管理 / HTTP Service
java·开发语言·spring boot·后端·python·spring·http
AI大佬的小弟6 小时前
Python基础(10):Python函数基础详解
开发语言·python·函数·ai大模型基础·嵌套函数·变量的作用域·全局变量和局部变量
Evand J6 小时前
【2026课题推荐】基于累计概率方法匹配轨迹的飞行目标轨迹定位,附MATLAB代码的演示效果
开发语言·matlab·目标跟踪·定位·轨迹匹配
_200_6 小时前
Lua 基本数据类型
开发语言·junit·lua
人邮异步社区6 小时前
C++之父的《C++程序设计语言》(第4版)重译出版!
java·jvm·c++
墨有6666 小时前
C++ 模板入门:从函数模板到类模板
c++