条款1:理解模板类型推导

news2025/1/24 5:07:20

现代C++中被广泛应用的auto是建立在模板类型推导的基础上的。而当模板类型推导规则应用于auto环境时,有时不如应用于模板中那么直观。由于这个原因,真正理解auto基于的模板类型推导的方方面面非常重要。

在c++中声明一个模板函数的伪代码基本如下:

template<typename T>
void f(ParamType param);

它的调用看起来像这样

f(expr);                        //使用表达式调用f

在编译期间,两个类型T和ParamType通常是不同的。因为ParamType包含一些修饰,比如const和引用修饰符。例如:

template<typename T>
void f(const T& param);         //ParamType是const T&

//调用情况
int x = 0;
f(x);            //ParamType被推导为const int&,T被推导为int。

所以,T的类型推导不仅取决于expr的类型,也取决于ParamType的类型

这里有三种情况:

  • ParamType是一个指针或引用,但不是通用引用
  • ParamType是一个通用引用
  • ParamType既不是指针也不是引用(传值方式(pass-by-value))

 测试数据:

用变量、引用、常量、右值引用、对常量的引用、指针、指针常量、常量指针、指向常量的指针常量 、数组来进行测试

    int                i = 0;
    int&               Ref = i;
    int&&              rRef=2;
    const int          Const = 2;
    const int&         ConstRef = i;
    int*               Pointer = &i;
    int* const         PointerConst = &i;
    const int*         ConstPointer=&i;
    const int* const   ConstPointerConst = &i;

    char CharArr[]="wosh";
    const char ConstCharArr[]="wosh";

推荐一个测试网站,可以查看推导出来的类型。https://cppinsights.io/

 

1.ParamType是一个指针或引用,但不是通用引用

1.1ParamType是一个引用

T&和const T&

推导规则(得到T的类型):

        忽略引用部分,保留cv限定符

那么,ParmType就一定是左值引用类型,ParmType去掉引用符号后,剩下的就是T类型

 注意的事项:

由于引用的特性,形参就代表实参本身,所以实参expr的cv限定符会被T保留下来的若传进来的实参是个指针,那param会将实参(即指针)的top-level和low-level const都保存下来的

(不明白top-level含义的请看常量指针和指针常量, top-level const和low-level const)

1.1.1 ParmType是 T&

测试的模板函数:

template<typename T>
void FunRef(T& param) {}


FunRef(i);		            // ParamType:int& ,	            T:int
FunRef(Ref);		        //ParamType:int& 	            T:int,  
FunRef(rRef);		        // ParamType:int&               T:int,  
FunRef(Const);		        //ParamType:cosnt int&          T:const int,  
FunRef(ConstRef);		    //ParamType:cosnt int&          T:cosnt int,  
FunRef(Pointer);		    // ParamType:int* &,            T:int*, 
FunRef(PointerConst);		//ParamType:int* cosnt&         T:int* const,  
FunRef(ConstPointer);		//ParamType:const int* &        T:const int*,  
FunRef(ConstPointerConst);	//ParamType:cosnt int* const &, T:cosnt int* const,  

//数组类型对于T&有点特别,不会退化成指针的
FunRef(CharArr);		//ParamType:char(&)[5]        T:char[5]
FunRef(ConstCharArr);	//ParamType:const char(&)[5]        T:const char[5] 

1.1.2 ParmType是const T&

与T&的区别是ParmType是top-level(顶层)const类型

那么ParmType去掉顶层const和引用符号就是T类型。(即T一定不是顶层const类型)

测试的模板函数:

template<typename T>
void FunConstRef(const T& param) {}

//以下情况ParmType都是const int& ,T都是int
FunConstRef(i);		
FunConstRef(Ref);		
FunConstRef(rRef);	
FunConstRef(Const);		
FunConstRef(ConstRef);
		
FunConstRef(Pointer);		    //ParmType: int* const &        T:int*,  
FunConstRef(PointerConst);		// ParmType: int* cosnt&        T:int*, 
FunConstRef(ConstPointer);		// ParmType:const int* const&   T:const int*, 
FunConstRef(ConstPointerConst);	//ParmType:cosnt int* const &   T:cosnt int*,  

//数组类型和T&类似的
FunConstRef(CharArr);		//ParamType:const char(&)[5]        T:const char[5]
FunConstRef(ConstCharArr);	//ParamType:const char(&)[5]        T:const char[5] 

1.2 ParamType是一个指针

指针有4种情况:T*,const T*,T* const,const T* const。

注意事项:

传参时param指针是实参的副本,所以实参和形参是不同的两个指针。T在推导的时候,会保留实参中的low-level的const,舍弃top-level的const

low-level const修饰的是指针指向的对象,表示该对象不可改变,所以const 应该保留。而top-level const修饰的是指针本身,从实参到形参传递时复制的是指针,因此形参的指针只是个副本,所以不保留其const属性。

1.2.1 ParamType 是 T*

推导规则:

        和引用的类似,ParamType一定是non-const类型(传参时忽略top-level const)。

那么,ParamType去掉指针符号就是T类型

测试的模板函数:

template<typename T>
void FunPtr(T* param) {}

 测试结果:

//void FunPtr(T* param)
FunPtr(&i);			        //ParamType:int*       T:int    
FunPtr(&Ref);			    //ParamType:int*       T:int  
FunPtr(&rRef);			    //ParamType:int*       T:int      
FunPtr(&Const);			    //ParamType:const int* T:cosnt int    
FunPtr(Pointer);			//ParamType:int*       T: int   
FunPtr(PointerConst);		//ParamType:int*       T:int    
FunPtr(ConstPointer);		//ParamType:const int* T: const int   
FunPtr(ConstPointerConst);	//ParamType:const int* T: const int 

//数组会退化成指针
FunPtr(CharArr);		//ParamType:char*       T:char
FunPtr(ConstCharArr);	//ParamType:const char*        T:const char

 1.2.2 ParamType 是 T* const

 推导规则:

        和T*实际是同一个模板的,ParamType只是多了top-level const。T是不变的。

template<typename T>
void FunPtrConst(T* const param) {}

//测试结果
FunPtrConst(&i);			    //ParamType:int* const   T:int    
FunPtrConst(&Ref);			    //ParamType:int* const   T:int  
FunPtrConst(&rRef);			    //ParamType:int* const   T:int      
FunPtrConst(&Const);			//ParamType:const int* const T:cosnt int    
FunPtrConst(Pointer);			//ParamType:int* const      T: int   
FunPtrConst(PointerConst);		//ParamType:int* const      T:int    
FunPtrConst(ConstPointer);		//ParamType:const int* const T: const int   
FunPtrConst(ConstPointerConst);	//ParamType:const int* const  T: const int 

//数组会退化成指针
FunPtrConst(CharArr);		//ParamType:char* const      T:char
FunPtrConst(ConstCharArr);	//ParamType:const char* const       T:const char 

1.2.3 ParamType 是 const T* const 或const T*

推导规则:

  • ParamType 是 pointer to const,则T 一定是不带 const 的非指针类型
templete<typename T>
void FunConstPtrConst(const T* const param);

//以下,ParamType都是const int* const   T都是int 
FunConstPtrConst(&i);			   
FunConstPtrConst(&Ref);			
FunConstPtrConst(&rRef);			 
FunConstPtrConst(&Const);			  
FunConstPtrConst(Pointer);			
FunConstPtrConst(PointerConst);			  
FunConstPtrConst(ConstPointer);			 
FunConstPtrConst(ConstPointerConst);

//数组会退化成指针     ParamType都是const char* const,      T都是char   
FunConstPtrConst(CharArr);	
FunConstPtrConst(ConstCharArr);	


templete<typename T>
void FunConstPtr(const T* param);

//以下,ParamType都是const int*   T都是int 
FunConstPtr(&i);			   
FunConstPtr(&Ref);			
FunConstPtr(&rRef);			 
FunConstPtr(&Const);			  
FunConstPtr(Pointer);			
FunConstPtr(PointerConst);			  
FunConstPtr(ConstPointer);			 
FunConstPtr(ConstPointerConst);

//数组会退化成指针    ParamType都是const char* , T都是char   
FunConstPtr(CharArr);		
FunConstPtr(ConstCharArr);

2.ParamType是通用引用

  • 如果expr是左值,TParamType都会被推导为左值引用。这非常不寻常,第一,这是模板类型推导中唯一一种T被推导为引用的情况。第二,虽然ParamType被声明为右值引用类型,但是最后推导的结果是左值引用。
  • 如果 expr 是右值,则 ParamType 推断为右值引用类型,去掉 && 就是 T 的类型,即 T 一定不为引用类型
template <typename T>
void f(T&& x);

f(i);	//i是左值, ParamType:int&  T:int&
f(rRef);	//rRef是右值引用,也就是左值 ParamType:int&  T:int&
f(Const);	//Const是左值 ParamType:const int&  T:const int&
f(ConstRef);	//ConstRef是左值, ParamType:const int&  T:const int&
f(2);		//2是右值, ParamType:int&&  T:int

3.ParamType既不是指针也不是引用

 通过传值的方式处理,其模板如下:

template<typename T>
void f(T param);                //以传值的方式处理param

推导规则:

  • 如果expr是一个引用,忽略引用部分。
  • 如果expr带有constvolatile,忽略constvolatile

 即是丢弃 expr 的 top-level cv 限定符和引用限定符

 测试结果:

//void Fun(T param)
//以下 ParamType都是int,	T都是int
Fun(i);		        
Fun(Ref);		    
Fun(rRef);		   
Fun(Const);		    
Fun(ConstRef);	
	 
Fun(Pointer);		//ParamType:int*     T:int*,
Fun(PointerConst);		//ParamType:int*  T:int*,  
Fun(ConstPointer);		//ParamType:const int*  T:const int*,  
Fun(ConstPointerConst);		//ParamType:cosnt int* , T:const int*

//数组退化成指针
Fun(CharArr);		//ParamType:char* , T:char*
Fun(ConstCharArr);		//ParamType: coonst char* , T: const char*

4.特别情况:expr是函数名

 数组可以退化为指针,函数也可以退化为指针

void someFunc(int);         //someFunc是一个函数,类型是void(int)

template<typename T>
void f1(T param);                   //传值给f1

template<typename T>
void f2(T & param);                 //传引用给f2

template<typename T>
void f3(T && param);                 //传通用引用给f3

template<typename T>
void f4(T* param);                 //传指针给f4

f1(someFunc);     //param被推导为指向函数的指针,类型是void(*)(int),T也是param类型
f2(someFunc);     //param被推导为指向函数的引用,类型是void(&)(int),T是void()(int)
f3(someFunc);     //someFunc是左值,被推导为左值引用,parm类型是void(&)(int),T和param类型一致
f3(someFunc);     //param被推导为指向函数的指针,类型是void(*)(int),T是void()(int)

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/521715.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JVM 直接内存(Direct Memory)

直接内存概述 不是虚拟机运行时数据区的一部分&#xff0c;也不是<<Java 虚拟机规范>> 中定义的内存区域直接内存是Java 堆外的、直接向系统申请的内存区间来源于 NIO&#xff0c;通过存在堆中的 DirectByteBuffer 操作 Native 内存访问直接内存的速度会优于 Java…

智慧停车APP系统开发 停车取车缴费智能搞定

生活水平的提高让车辆成为很多人出行主要的代步工具&#xff0c;很多家庭现在已经不止拥有一辆汽车了&#xff0c;所以城市建设中关于停车场的规划管理也是很重要的部分。不过现在出门很多时候还是会碰到找不到停车场&#xff0c;没有车位、收费不合理、乱收费等现象。智慧停车…

调试和优化遗留代码

1. 认识调试器 1.1 含义 一个能让程序运行、暂停、然后对进程的状态进行观测甚至修改的工具。 在日常的开发当中使用非常广泛。(PHP开发者以及前端开发者除外) 1.2 常见的调试器 Go语言的自带的 delve 简写为 “dlv”GNU组织提供的 gdbPHP Xdebug前端浏览器debug 调试 1.3…

DNS投毒

定义 DNS缓存投毒又称DNS欺骗,是一种通过查找并利用DNS系统中存在的漏洞,将流量从合法服务器引导至虚假服务器上的攻击方式。与一般的钓鱼攻击采用非法URL不同的是,这种攻击使用的是合法URL地址。 DNS缓存中毒如何工作 在实际的DNS解析过程中,用户请求某个网站,浏览器首…

English Learning - L3 作业打卡 Lesson1 Day6 2023.5.10 周三

English Learning - L3 作业打卡 Lesson1 Day6 2023.5.10 周三 引言&#x1f349;句1: The expression was first used in America at the beginning of the twentieth century .成分划分弱读连读爆破语调 &#x1f349;句2: It probably comes from the fact that many babies…

分享一组有意思的按钮设计

先上效果图&#xff1a; 一共16个&#xff0c;每个都有自己不同的样式和效果&#xff0c;可以用在自己的项目中&#xff0c;提升客户体验~ 再上代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8">&l…

非Autosar软件手动集成XCP协议栈

文章目录 前言XCP发送XCP接收Xcp初始化Xcp主函数Xcp Event总结前言 最近项目由于各种原因没有直接采用基于Autosar工具生成的代码。只使用了NXP的MCAL。Demo需求实现XCP功能。本文记录手动集成XCP协议的过程,基于CAN总线。集成的前提过程是已有了XCP的静态代码和配置代码。可…

数据结构pta第一天: 堆中的路径 【用数组模拟堆的操作】

这道题其实就涉及两个堆操作&#xff0c; 一个是插入&#xff0c;一个是通过从底到根的遍历 堆的插入&#xff1a;其实就是从下面往上&#xff0c;一个一个比较&#xff0c;&#xff08;因为上面的节点里的值越来越小&#xff0c;如果插入的值比上面的节点小那么就要向上推&am…

基于AT89C51单片机的电子时钟设计与仿真

点击链接获取Keil源码与Project Backups仿真图&#xff1a; https://download.csdn.net/download/qq_64505944/87779867?spm1001.2014.3001.5503 源码获取 主要内容&#xff1a; 使用DS1302芯片作为计时设备&#xff0c;用6个7段LED数码管或者LCD162作为显示设备&#xff0c…

【软考七】面向对象技术--UML、设计模式(分数重,刷题刷题)

建议UML和设计模式去听听课&#xff0c;内容多&#xff0c;还需要记。这一部分内容较多&#xff0c;下半年的考生可以慢慢看&#xff0c;上半年的就去刷题吧。 该博客不适合学习UML和设计模式&#xff0c;只适合考试。要学的不要在这浪费时间&#xff0c;切记切记 在5月13号忽然…

MD-MTSP:孔雀优化算法POA求解多仓库多旅行商问题(提供MATLAB代码,可以修改旅行商个数及起点)

一、多仓库多旅行商问题 多旅行商问题&#xff08;Multiple Traveling Salesman Problem, MTSP&#xff09;是著名的旅行商问题&#xff08;Traveling Salesman Problem, TSP&#xff09;的延伸&#xff0c;多旅行商问题定义为&#xff1a;给定一个&#x1d45b;座城市的城市集…

Midjourney8种风格极其使用场景

目录 ​编辑 引言 等距动画 场景 分析性绘图 场景 着色书 场景 信息图画 场景 双重曝光 场景 图示性绘画 场景 二维插图 场景 图解式画像 场景 总结&#xff1a; 八种风格箴言&#xff1a; 引言 我相信大家都或多或少玩过Midjourney&#xff0c;但是要形…

linux系统sed编辑器

sed编辑器 sed编辑器sed基础语法sed查询sed删除sed 替换sed 插入 sed编辑器 sed是文本处理工具&#xff0c;依赖于正则表达式&#xff0c;可以读取文本内容&#xff0c;工具指定条件对数据进行添加、删除、替换等操作&#xff0c;被广泛应用于shell脚本&#xff0c;以完成自动…

我所了解的老板

我所了解的老板 修心篇以名命物 扰我心良心的本来如此 是我心不要全挑剔别人 既是我因 受我之果长久之爱 美女养眼 贤妻养心 调研篇深度思考确定趋势确定时机预测筹划生存资料 做事篇做短视频心态&#xff0c;也是创业的心态做销售冠军的心态&#xff0c;也是创业的心态 修心篇…

【Python--定时任务的四种方法】

定时任务 前言while True&#xff1a;sleep()优点缺点 threading.Timer定时器多线程执行优点缺点 Timeloop库执行定时任务调度模块schedule优缺点 前言 当每隔一段时间就要执行一段程序&#xff0c;或者往复循环执行某一个任务&#xff0c;这就需要使用定时任务来执行程序。应…

【Unity项目实战】手把手教学:飞翔的小鸟(7)障碍对象池

承接上一篇&#xff1a;【Unity项目实战】手把手教学&#xff1a;飞翔的小鸟&#xff08;6&#xff09;添加障碍&#xff0c;我们已经生成了一个障碍物柱子&#xff0c;并且使得小鸟在越过柱子之后自动获得一分&#xff0c;接下来将继续讲解障碍物的随机生成。 一.脚本控制 障…

Centos7设置静态IP

Centos7设置静态IP及修改后不能联网问题解决方式 虚拟机外部配置1、打开Vmware选择虚拟网络编辑器菜单2、获取管理员权限&#xff0c;选择VMnet8&#xff0c;并点击还原默认设置&#xff0c;弹窗选择“是”。3、修改VMnet8的参数4、NAT设置5、确认虚拟机为NAT模式 虚拟机内部配…

数据流图(DFD)

目录 第十二章、数据流图&#xff08;DFD&#xff09;1、数据流图基本概念2、数据字典3、数据平衡原则4、数据流图试题解题技巧4.1、试题一4.2、试题二 第十二章、数据流图&#xff08;DFD&#xff09; 1、数据流图基本概念 数据流图是用于表示系统逻辑模型的一种工具。从数据…

【Swift】Swift和Objective-c混编

1.介绍 Swift和Objective-C都是苹果公司的编程语言&#xff0c;它们可以在同一个项目中同时使用。这种混编方式被称为“混合编程”&#xff08;Mixed Programming&#xff09;。 在混合编程时&#xff0c;我们需要用到一个桥接文件&#xff08;Bridging Header&#xff09;&a…

AfxMessageBox的两种用法

1 函数简介 函数声明如下&#xff1a; int AfxMessageBox(LPCTSTR lpszText,UINT nType MB_OK,UINT nIDHelp 0 ); int AFXAPI AfxMessageBox(UINT nIDPrompt,UINT nType MB_OK,UINT nIDHelp (UINT ) -1 );1.1 参数 lpszText 将显示在消息框的字符串。 nType 消息框…