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

现代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)

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

六、IDEAJ同一个服务启动多台服务器的方法

目录 1、打开启动类配置窗口--->选择Edit Configurations进入配置窗口 2、从左侧Springboot应用选择需要启动的多台服务器&#xff08;服务只要启动一次就会在此窗口有显示&#xff09;--->勾选Allow parallel run菜单&#xff08;默认不勾选&#xff0c;则只能启动一台…

Springboot +Flowable,会签、或签简单使用(一)

一.简介 **会签&#xff1a;**在一个流程中的某一个 Task 上&#xff0c;这个 Task 需要多个用户审批&#xff0c;当多个用户全部审批通过&#xff0c;或者多个用户中的某几个用户审批通过&#xff0c;就算通过。 例如&#xff1a;之前的请假流程&#xff0c;假设这个请假流程…

板材激光切割机切割穿孔时注意的几个问题

激光切割设备广泛应用于钣金、五金制品、钢结构、汽车配件、广告、工艺品等行业&#xff0c;成为加工行业不可缺少的环节。在厚板加工中穿孔时间占很大比重&#xff0c;随着加工板材越来越厚&#xff0c;板材激光切割机切割穿孔也会相应地增加难度。 激光切割机两种常见的穿孔方…

Linux环境下编程遇到“fatal error:stdio.h:没有那个文件或目录”错误解决办法

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天总结一下linux环境下如何解决一个常见的问题&#xff0c;也就是“fatal error:stdio.h:没有那个文件或目录”错误。 不少初学者在linux环境下用gcc编译C语言时&#xff0c;经常会遇到这个问题。 比如当…

涂鸦智能生活App SDK:全量级灵活定制,让你的App更具差异化

之前一期&#xff0c;我们介绍过涂鸦 OEM App 开发方案&#xff08;点击查看往期介绍&#xff09;&#xff0c;它集品牌 UI 自定义、服务、运营、商城营销于一体&#xff0c;无需代码&#xff0c;开发者点选拖拽即可快速配置想要的常用功能&#xff0c;最快 10 分钟即可完成一款…

一文打尽目标检测NMS(1): 精度提升篇

文章来自于&#xff1a;曲終人不散丶知乎&#xff0c; 连接&#xff1a;https://zhuanlan.zhihu.com/p/151914931&#xff0c; 本文仅用于学术分享&#xff0c;如有侵权&#xff0c;前联系后台做删文处理。 众所周知&#xff0c;非极大值抑制NMS是目标检测常用的后处理算法&…

测试的分类(按测试对象、是否查看代码、开发阶段、实施组织...... + 思维导图总结)

目录 一、测试的分类 1. 按测试对象划分 2. 按照是否查看代码划分 3. 按照开发阶段划分 4. 按照测试实施组织划分 5. 按照是否运行划分 6. 按照是否手工划分 7. 按照地域划分 二、总结 一、测试的分类 1. 按测试对象划分 &#xff08;1&#xff09;界面测试 简称UI测…

自学黑客(网络安全)如何入门?收藏这篇就够了

前言&#xff1a; 趁着今天下班&#xff0c;我花了几个小时整理了下&#xff0c;非常不易&#xff0c;希望大家可以点赞收藏支持一波&#xff0c;谢谢。 我的经历&#xff1a; 我19年毕业&#xff0c;大学专业是物联网工程&#xff0c;我相信很多人在象牙塔里都很迷茫&#x…

从零开始写一个Vue3+Element Plus的后台管理系统

写在开始之前 接触Vue3也有一年的时间了&#xff0c;除了刚开始用Vue3做了一个小小的项目&#xff0c;其后一直没有机会在项目中真正使用Vue3&#xff0c;反而一直维护Vue2的老项目。作为一个有追求&#xff08;wuliao&#xff09;的前端&#xff0c;那就自己开一个git仓库练手…

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

点击链接获取Keil源码与Project Backups仿真图&#xff1a; https://download.csdn.net/download/qq_64505944/87765092?spm1001.2014.3001.5503 源码获取 运用单片机&#xff0c;将音乐的大部分音符与相应按键相匹配&#xff0c;让音乐爱好者利用单片机也可以进行演奏。 基…

前端架构师-week6- ejs源码讲解

ejs 源码详解——彻底搞懂模版动态渲染原理 ejs 执行流程 源码实现思路非常有趣&#xff0c;实现难度实现流程不如前几章源码复杂。但是思路完全不同&#xff0c;用了一套新的思路来实现 ejs 模版渲染。重要的是通过这种思路开阔自己的眼界。ejs 的思路在 vue 中也有用到。 核…

VMware安装Ubuntu系统

VMware安装Ubuntu系统 1.首先选择文件&#xff0c;点击新建虚拟机 2.选择自定义&#xff0c;点击下一步 3.点击下一步 4.选择稍后安装操作系统&#xff0c;点击下一步 5.选择Linus操作系统&#xff0c;版本选择Ubuntu64位&#xff0c;点击下一位 6.自己看图 7. 这里根据自…

Leetcode50. Pow(x, n)

Every day a Leetcode 题目来源&#xff1a;50. Pow(x, n) 解法1&#xff1a;递归 代码&#xff1a; /** lc appleetcode.cn id50 langcpp** [50] Pow(x, n)*/// lc codestart class Solution { public:double myPow(double x, int n){if (n 0)return 1.0;if (n < 0)re…

必定收藏:国内免费可用 ChatGPT 网页版

ChatGPT是一个基于人工智能的聊天机器人&#xff0c;它可以与用户进行自然语言交互。ChatGPT使用了最新的自然语言处理技术&#xff0c;包括深度学习和神经网络&#xff0c;以便更好地理解用户的意图和回答用户的问题。 ChatGPT可以回答各种问题&#xff0c;包括但不限于常见问…

让chatGPT给我写一个CSS,我太蠢了

前言 CSS这东西&#xff0c;让AI写的确有点难度&#xff0c;毕竟它写出来的东西&#xff0c;没办法直接预览&#xff0c;这是其次。重要的是CSS这东西怎么描述&#xff0c;不好描述啊&#xff0c;比如我让他给我制作一个这样的效果出来&#xff0c;没办法描述&#xff0c;所以…

5.13下周黄金最新行情走势分析及多空交易策略

近期有哪些消息面影响黄金走势&#xff1f;下周黄金多空该如何研判&#xff1f; ​黄金消息面解析&#xff1a;周五(5月12日)美市尾盘&#xff0c;现货黄金收报2010.23美元/盎司&#xff0c;下跌4.57美元或0.23%&#xff0c;日内最高触及2022.59美元/盎司&#xff0c;最低触及…

小程序组件

swiper swiper 和 swiper-item 结合使用&#xff0c;&#xff0c; swiper有默认高度 300rpx 属性&#xff1a; autoplay &#xff1a; 自动播放circular &#xff1a; 循环播放indicator-dots &#xff1a; 显示指示点indicator-active-color &#xff1a; 轮播选中的颜色 &…

cookie、session、token的区别是什么

前言 今天就来说说session、cookie、token这三者之间的关系&#xff01;最近这仨玩意搞得头有点大&#x1f923; 1.为什么会有它们三个&#xff1f; 我们都知道 HTTP 协议是无状态的&#xff0c;所谓的无状态就是客户端每次想要与服务端通信&#xff0c;都必须重新与服务端链接…

空调群控、智能报警,这些设备有点牛

空调绝对是夏季使用率高的电器之一&#xff0c;尤其是今年全国各地高温不断&#xff0c;说“命都是空调给的”都不为过...... 在家的时候&#xff0c;我们可以随手开关空调&#xff0c;非常方便&#xff0c;如果是学校教学楼、工厂宿舍、银行网点、办公大楼、机房等地的管理者…

一个打通基于XML管理Bean

目录 准备工作 添加依赖 引入配置文件 获取bean ①方式一&#xff1a;根据id获取 ②方式二&#xff1a;根据类型获取 ③方式三&#xff1a;根据id和类型 ④注意的地方 ⑤扩展知识 依赖注入之setter注入 ①创建学生类Student ②配置bean时为属性赋值 ③测试 依赖注…