当前位置: 首页 > news >正文

C++ 中的万能引用、引用折叠、完美转发

在学习c++过程中,相信不少同学都或多或少的听过万能引用、引用折叠、完美转发,介绍这几个概念之前,首先列举下左值和右值的概念;

左值和右值:

顾名思义,可以简单的理解为在等号左边的值是左值,再等号右边的值是右值,例如 int a = 1,那个a就是左值,而1就是右值,左值可以用&标志符取地址,右值不行;另外,临时变量也是属于右值,例如:

using namespace std;
class A
{

};

int main()
{
	A a = A();
}

这里a是左值,A()是属于临时变量,是属于右值

左值引用和右值引用:

左值引用用一个&符号进行标志,例如

int main()
{
	int a = 10;
	int & left_value = a;
	//或者用const修饰
	const int& left_value1 = 100;
}

这里left_value就是左值引用, left_value1也是左值引用,由于有const进行修饰,所以可以直接赋右值;


右值引用用标志符号&&表示,例如

int main()
{
	int&& a = 100;
}

这里right_value就是右值引用。
这里小结一下,其实无论是左值、右值还是左值引用、右值引用,都是一种类型,就可我们平时定义类型int float、double等一样,都对应这一种定义,知道他们是不用的类型就可以。

万能引用:

万能引用通常是在模板中使用,例如T &&这种模板的定义,它既可以接受左值,右值,也可以接收左值引用、右值引用作为参数,所以成为万能引用,而且在使用的过程中,我还发现这种可以不用给定模板类型,说明使用万能引用可以实现自动推断,如下面的例子:

#include<iostream>
using namespace std;

template<typename T>
void print(T& a)  // 接受左值
{
	cout << "左值" << endl;
}

template<typename T>
void print(T&& a) //接受右值
{
	cout << "右值" << endl;
}

template<typename T>
void forwardTest(T&& v)
{
	print(v);
	print(move(v));
	print(forward<T>(v)); //利用std::forward<T>(u)进行转发,当且仅当T为左值引用时,u被转换为左值
	//引用,否者全都转换为右值引用
}

int main()
{
	int a = 10;
	string s = "asd";
	forwardTest(a);
	cout << "------------------" << endl;
	forwardTest(move(s));
}

/*
输出为:
左值
右值
左值
------------------
左值
右值
右值*/

在模板函数 forwardTest中,模板参数类型是T && ,这个就是万能引用的标志,然后我们可以用这个函数把穿进去参数,根据左值和右值进行完美转发。

引用折叠:

所谓的引用折叠,就是当有多个&标志的时候,进行类型的推断,规则是当且仅当T的类型是&& 右值的时候,根据引用折叠推断出来的类型是右值,否者都是左值引用,例如
转换规则是:

  • && && ==> &&
  • & && ==> &
  • && ==>&

或者换种说话就是:

  • 当T的类型是 int &&,代入万能引用模板就是 int && &&,等价于int &&,也就是右值引用。
  • 当T的类型是int &,代入万能引用模板就是 int & && ,等价于iint &,也就是左值引用。
  • 当T的类型是int ,代入万能引用模板就是 int &&,这个等价于int &,也就是左值引用。

上面三类就是引用折叠的定义。

完美转发:

所谓的完美转发,就是当我传左值和右值引用到模板函数中时,在模板函数中使用这个参数,要能够区分左值和右值引用,在使用std:forward之前会有什么问题呢,还是上面万能引用中提到的代码,如下:

#include<iostream>
using namespace std;

template<typename T>
void print(T& a)  // 接受左值
{
	cout << "左值" << endl;
}

template<typename T>
void print(T&& a) //接受右值
{
	cout << "右值" << endl;
}

template<typename T>
void forwardTest(T&& v)
{
	print(v);
	print(move(v));
	print(forward<T>(v)); //利用std::forward<T>(u)进行转发,当且仅当T为左值引用时,u被转换为左值
	//引用,否者全都转换为右值引用
}

int main()
{
	int a = 10;
	string s = "asd";
	forwardTest(a);
	cout << "------------------" << endl;
	forwardTest(move(s));
}

/*
输出为:
左值
右值
左值
------------------
左值
右值
右值*/

在forwardTest函数中,第一个print函数的调用始终是左值,这是因为无论是左值还是右值,进入到函数中时,都会变成左值,为什么呢?因为它有了地址,所以就是左值了,所以为了解决这种问题,std:forward就开始显示它的作用了,我们需要利用到forward进行转发,让传进去的值,可以按照其左右值的类型进行分发,类似于上面代码的print,两个重载函数,可以根据参数类型进行对应的分发。

相关文章:

  • 软件架构模式
  • Eclipse Theia技术揭秘——脚手架源码分析
  • React AntV/G2Plot环形图Pie添加点击事件,即点击图环触发获取相关数据。
  • 导航【Java设计模式】
  • .NET MAUI学习笔记——2.构建第一个程序_初级篇
  • JavaScript4种数组随机选取实战源码
  • Python 教程之 Numpy(1)—— 什么是 Numpy?
  • $refs 、$nextTic、动态组件、name的使用
  • C语言实现简单通讯录,malloc,calloc,realloc,free动态内存分配的学习。
  • 【新人报到】【学习笔记】Python编程——入门
  • 【金融】中国vix、skew指数的Python实现
  • 外汇天眼:英镑暴跌英国国债暴跌 英国新政府宣布激进的减税促经济计划
  • 基于React的富文本编辑器——Braft Editor使用
  • vue项目docker打包通过k8s
  • 论文教程之阅读科学论文11步法,详细介绍了每个部分的关注点
  • [ 一起学React系列 -- 8 ] React中的文件上传
  • [译] 怎样写一个基础的编译器
  • ABAP的include关键字,Java的import, C的include和C4C ABSL 的import比较
  • Angular2开发踩坑系列-生产环境编译
  • canvas 高仿 Apple Watch 表盘
  • Python代码面试必读 - Data Structures and Algorithms in Python
  • React-redux的原理以及使用
  • SAP云平台运行环境Cloud Foundry和Neo的区别
  • 阿里云爬虫风险管理产品商业化,为云端流量保驾护航
  • 编写高质量JavaScript代码之并发
  • 关于使用markdown的方法(引自CSDN教程)
  • 最近的计划
  • No resource identifier found for attribute,RxJava之zip操作符
  • 7行Python代码的人脸识别
  • 东超科技获得千万级Pre-A轮融资,投资方为中科创星 ...
  • 曜石科技宣布获得千万级天使轮投资,全方面布局电竞产业链 ...
  • ( 10 )MySQL中的外键
  • (2)nginx 安装、启停
  • (20)目标检测算法之YOLOv5计算预选框、详解anchor计算
  • (2022版)一套教程搞定k8s安装到实战 | RBAC
  • (4)通过调用hadoop的java api实现本地文件上传到hadoop文件系统上
  • (bean配置类的注解开发)学习Spring的第十三天
  • (C#)if (this == null)?你在逗我,this 怎么可能为 null!用 IL 编译和反编译看穿一切
  • (Mac上)使用Python进行matplotlib 画图时,中文显示不出来
  • (Matlab)遗传算法优化的BP神经网络实现回归预测
  • (求助)用傲游上csdn博客时标签栏和网址栏一直显示袁萌 的头像
  • (转)C#调用WebService 基础
  • (转)编辑寄语:因为爱心,所以美丽
  • * 论文笔记 【Wide Deep Learning for Recommender Systems】
  • .NET/C# 使窗口永不获得焦点
  • .NET/C# 使用反射注册事件
  • .net专家(张羿专栏)
  • @RunWith注解作用
  • [20161214]如何确定dbid.txt
  • [20180129]bash显示path环境变量.txt
  • [Avalon] Avalon中的Conditional Formatting.
  • [CSS]CSS 的背景
  • [EFI]英特尔 冥王峡谷 NUC8i7HVK 电脑 Hackintosh 黑苹果efi引导文件
  • [Firefly-Linux] RK3568修改控制台DEBUG为普通串口UART
  • [JavaWeb]—Spring入门