《Beginning C++20 From Novice to Professional》第十章 Function Templates

C++ Template 基础篇(一):函数模板_函数模板的定义及使用-CSDN博客

这篇博客提到模板是泛型编程的基础,把类型也当做参数,这样使得静态类型语言对类型的处理更强大,提高了代码的可重用性,目标和软件工程一致,提供可复用的代码

本章可以学到下面内容:

Function Templates

函数模板本身不是函数,它是一种“食谱”或者“图纸”,表示一系列的函数声明;模板是参数化的函数定义,函数的实例需要有参数才可以创建,也就是说非必要时不会生成函数定义

由函数模板生成函数的过程叫作实例化instantiation,模板的参数通常是类型,但也有可能是其他一些值,比如维度等

我们来看一个之前提到过的例子,函数模板语法大致如上,尖括号里是模板参数,然后把这里声明的参数当做正常类型使用即可

这里的T叫做模板类型参数,我们可以用typename关键字,也可以用class关键字,但是更推荐前者

Creating Instances of a Function Template

举个使用的例子↑

可以看到我们就像使用普通函数一样使用函数模板,T一般是不需要我们指定的,编译器会去进行推导,这种机制叫模板实参推导;编译器首先查找有没有double参数的larger版本函数,如果没有就会通过模板实例化生成一个double larger()

直到这里,实例化的过程就完成了;每个版本的函数只会生成一次,即使不同文件都包含类似的模板;再举一个例子:

Template Type Parameters

一般提到parameter,指的都是形参,是函数定义时的理想参数,而不是调用函数时传入的那个值

形参T可以实例化为很多类型,前面说过string不适合复制,所以我们可以使用const引用来写这个比较大小的函数:

事实上标准库里有一对max、min函数的实现逻辑就和这个例子类似:

Explicit Template Arguments

这里强调的是当调用实例的时候实参类型和模板矛盾的情况,举个例子:

这个larger我们用一个int和一个double传入函数再看,发现编译不通过,提示对于类型T有两个矛盾的推导类型,这说明虽然模板实现了一定程度上的类型参数化,但还是有死板的地方

为了解决这个问题,针对这里可以进行隐式转换的基本类型,模板的解决方法是这样的

我们需要在尖括号里显式指定需要的参数类型,这样的话编译器就不推导了,在调用时直接实例化一个larger<double>版本,并像普通函数一样对int进行隐式转换

所以我们如果显式指定int版本,那可预见的结果就是输出10和19,因为19.6是double会被转为int

Function Template Specialization

注意specialization和instantiation不是一个概念,前者指的是出现和模板类型不完全相符的类型参数,需要我们指定类型参数;后者指的是通过模板生成函数定义的过程,一般我们翻译两者为特化和实例化

如果我们传进去了地址,那么输出可能跟变量定义的顺序有关,因为此时的T会被推导为指针类型(存储的是两个变量的地址),而这两个局部变量的地址取决于编译器实现,所以这个输出是不可预料的

我们换了一下定义顺序结果就变了

a template specialization defines a behavior that is different from the standard template.

特化版本必须出现在标准版本之后实例化之前,否则无法编译

特化版本不需要尖括号里的形参T,因为我们指定了类型参数

不过书里说不建议特化函数模板,这种行为和typedef、new/delete一样是不推荐使用的语言特性

We recommend you never use function template specialization.

To customize a function template for specific types, you should overload the function template instead, either with a regular function or another function template.

We discuss these options in the next section.

Function overloads behave more intuitively in general than function template specializations.

Function Templates and Overloading

函数模板也是可以重载的,这样比模板特化更好,重载版本要比通用泛型版本更先被编译器选择

之前提到,同名函数只要参数列表不同就可以,虽然两个都可以调用,但是能不使用模板就不使用模板而是优先选择普通函数

甚至我们可以重载模板

注意这不是模板特化,这属于两个不同的模板,这个重载版本只有对指针类型参数才会实例化

重载也不是非要参数数量一致,重载个其他的版本也可以:

Function Templates with Multiple Parameters

之前提到的显式模板实参虽然可以解决不同类型参数推导矛盾的问题,但是更好的写法是加一个推导参数

对于a和b我们完全可以使用不同的类型T1,T2,这样参数可以被正确推导获取类型

但是有一个问题就是如果两个参数类型不同,返回值应该是什么类型,如这里的???位置应该是什么类型呢?

有一种解决办法是再加一个类型作为返回值,然后return的时候都转换为返回值类型:

这三行输出都指定了返回值类型是int,后两个则是对比较的两个数的类型进行了说明,这样似乎能解决问题

但是我们还是想做到不指定类型的编写方式,毕竟泛型要是指定类型也就不叫泛型了,一切类型都应该推导得来而不是写死在代码里

下一部分我们会讲到返回值推导,应该能做到自然的写法也能得出正确结果:

Return Type Deduction in Templates

针对上一小节三个问号那里的返回值应该怎么写,其实没有很好的解决方法,但是我们可以让编译器对这里的类型进行推导:

这样写的话虽然ab参数类型不同,但是还是可以返回较大值的类型的:

这里是输出正常结果的

decltype(auto)

考虑一个问题,如果我们这么写但是想要返回一个引用的时候,用auto该怎么做呢?因为auto的推导永远都是值类型,不会推导出指针或者引用

那么我们只能显式加上一个引用符号

但是这样又会出现第八章讲函数那里提到的问题,如果形参和实参不是一个类型,虽然是引用但还是会产生临时对象,我们的返回值还是一个局部对象的引用

这里程序不会输出正确结果,返回了一个错误代码,输出的编译信息是这样的

GDB单步调试发现这里的返回语句出现了段错误


针对这种情况,我们只能使用decltype(auto)这个语法来进行合适的类型推导

书上对这一点没有很详细的解释,我们可以看stackoverflow上有一个提问对于这一点的解释

c++ - What are some uses of decltype(auto)? - Stack Overflow

When to use decltype(auto) versus auto? - C++ Forum

后来发现这里的第二篇论坛解释更清楚,明确说到如果我们不知道函数要返回什么类型,或者是要根据参数类型来确定返回类型,那么我们不能写死为auto或者auto&,因为这两者的类型是明确的,要么是值要么就是引用,不灵活

所以这种模板里面一般使用decltype(auto),不是泛型函数就写死吧

(这里扯到了完美转发的技术,后面再说)

Default Values for Template Parameters

模板类型形参也是可以有默认值的

我们可以让他的默认返回值为double类型,当我们不指出返回类型的时候,也就是尖括号不用的时候,返回类型就是double

但是这里只说可以这么写没说这样写在larger函数里是好的做法,因为我们不是所有情况都想要double返回值;而且第二个重点是模板参数默认值和函数参数默认值不一样,可以有一定的灵活性

我们甚至还可以这么写:

用第一个参数来当第二个参数的默认值,只要T按顺序出现即可

不过由于标准库一般也是从右往左写,大家也都这么做了

Non-Type Template Parameters

假设我们要确认一个值是否在某区间里

对于C++20来说,模板形参可以是很多基本类型,数值、指针、引用、枚举等等,但是一般不写为class,限制很多我们不详细讲

由于这里非类型参数没有默认值,所以我们需要在尖括号里指出确定的区间两端分别是什么值

All template parameters, both type and non-type, need to be evaluated at compile time, while generating the concrete function instantiation from the template.

这里之所以把i定义为const,就是因为上面引用的这一条规则,模板参数必须是编译期可知的,否则无法生成函数实例

我们把const去掉就会发现无法编译:

如果我们不把这个范围边界写死为int,我们让编译器推导他的类型,使用起来会方便点

但是更好的做法是,把边界和传入的值设置为一个类型,即T:

虽然模板形参可以是非类型参数,但是一定要满足编译期可知的特点

Templates for Functions with Fixed-Size Array Arguments

这里的fixed-size指的是定长,即长度已经确定可知的情况,我们之前处理定长数组的方法是传一个引用

很明显如果我们不传引用的话就舍弃掉了数组的长度信息,但是有了模板我们就可以把长度也作为参数,此时编译器可以推导出这个长度:

其实标准库很多函数都是这么做的,让编译器去推导一个范围的长度,这样我们通过泛型就可以写出任意长度数组都可以用的函数了,这都归功于模板实参推导的机制

写一小段测试代码:

注意注释掉的第三个调用,充分地说明了数组名和指针本质不同

第四处我们传了一个列表,但是并不会生成参数是列表的新版本,而是接着用第二处调用生成的实例去推导参数

在C++20中我们也可以使用span来代替这种数组

如果类型是const T说明这种span不修改原序列,对const array也可以用

或者我们可以直接这样写:

这样定长不定长的序列我们都可以用,只不过不能修改序列内元素(不过求平均值确实也不用修改)

Abbreviated Function Templates

讲了这么多模板,其实模板写起来也挺冗余的,定义一个类型T就需要两个单词和一个括号显得有点繁琐

那么C++20对此做出的简化是

要注意,虽然不用template关键字,但这样写仍旧是函数模板

C++ template详解 | 普通人

这种写法叫做缩写函数模板,就是函数模板的语法糖,而且我们这里指的是参数列表中的auto而不是返回值的auto

每一个参数列表中的auto其实都会引入一个模板参数,语法糖就跟range-for类似,编译完之后还是有很多匿名的东西;当然我们也可以在尖括号里显式写出模板参数

这种写法涉及到编译问题,下一章会讲到

Limitations to Abbreviated Function Templates

语法糖香是香,但也是有限制的,像上面说到的,每一个参数列表中的auto都会引入一个新模板参数,所以如果我们想要两个参数类型相同的时候,就不能用这种缩写方式了

我们还是得采用传统写法,更别说函数内部使用参数类型了

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

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

相关文章

Java 框架安全:Spring 漏洞序列.(CVE-2022-22965)

什么叫 Spring 框架. Spring 框架是一个用于构建企业级应用程序的开源框架。它提供了一种全面的编程和配置模型&#xff0c;可以简化应用程序的开发过程。Spring 框架的核心特性包括依赖注入&#xff08;Dependency Injection&#xff09;、面向切面编程&#xff08;Aspect-Or…

【漏洞复现】CData API Server 路径遍历漏洞(CVE-2024-31849)

0x01 产品简介 CData API Server是CData公司的一个强大的数据连接平台&#xff0c;旨在帮助企业轻松地访问、整合和分析各种数据源。 0x02 漏洞概述 CData API Server 23.4.8846之前版本存在安全漏洞&#xff0c;该漏洞源于存在路径遍历漏洞。攻击者可利用该漏洞获得对应用程…

深度剖析Comate智能产品:科技巧思,实用至上

文章目录 Comate智能编码助手介绍Comate应用场景Comate语言与IDE支持 Comate安装步骤Comate智能编码使用体验代码推荐智能推荐生成单测注释解释注释生成智能问答 Comate实战演练总结 Comate智能编码助手介绍 市面上现在有很多智能代码助手&#xff0c;当时互联网头部大厂百度也…

Selenium——获取元素和操纵元素的方法

1、获取元素的方法 1、通过id获取 element wd.find_element(By.ID,"id")2、通过classname获取 elements wd.find_elements_by_class_name("plant") for element in elements:print(element.text)3、通过tagname获取元素 elements wd.find_elements_…

Node.js版本管理工具nvm的安装和使用

介绍 nvm全称 Node Version Manager 顾名思义它是用来管理 node 版本的工具&#xff0c;方便切换不同版本的Node.js。 使用 nvm的使用非常简单&#xff0c;跟npm的使用方法类似 下载 首先下载nvm&#xff0c;下载地址https://github.com/coreybutler/nvm-windows/releases…

目前最便宜的VPS多少钱一个月?

目前最便宜的VPS一个月的价格在5美元左右&#xff0c;换算成人民币约为35元。 VPS服务器的配置、性能、所在地区都是影响其价格的因素&#xff0c;价格与性能呈正相关&#xff0c;也有的廉价VPS的服务商会提供性能低的配置&#xff0c;让用户可以进行简单的网站托管或开发环境…

Elasticsearch FSCrawler 一个bug及解决方案

1、FSCrawler Bug 发现过程及描述 书接上一回&#xff0c;在使用 Elasticsearch FSCrawler 实现文档知识库检索的时候。 发现基于本地磁盘文件轮询导入 Elasticsearch 都没有问题。 但是&#xff0c;借助其 REST API 接口上传文件的时候&#xff0c;发现其字段 filesize 字段没…

安装Nox夜神模拟器关闭了HyperV后Docker运行不了怎么办?

1.背景 为了模拟真机&#xff0c;尝试安装了Nox夜神模拟器&#xff0c; 安装过程要求关闭Hyper-V。当时只是在程序安装卸载中关闭了系统服务。以为到时勾选上就好了。操作路径&#xff1a;控制面板\所有控制面板项\程序和功能\启用或关闭Windows功能\Hyper-V。 后来卸载掉了夜神…

C++ list 介绍

&#x1f308;一、认识list这个模版 ist是一个模版&#xff0c;需要结合一个具体的数据类型作为模版参数&#xff0c; 即list < T > <T> <T>&#xff0c;才能成为一个类类型。list是双向循环链表&#xff0c;是序列容器&#xff0c;允许在序列中的任何位置进…

智启算力平台基本操作

智启算力平台 智启算力平台路径搭载数据集搭载镜像配置 智启算力平台 开发文档 帮助文档 - OpenI - 启智AI开源社区 路径搭载 OpenIOSSG/promote: 启智AI协作平台首页推荐组织及推荐项目申请。 - notice/Other_notes/SDKGetPath.md at master - promote - OpenI - 启智AI开…

深入剖析Tomcat(七) 日志记录器

在看原书第六章之前&#xff0c;一直觉得Tomcat记日志的架构可能是个“有点东西”的东西。在看了第六章之后呢&#xff0c;额… 就这&#xff1f;不甘心的我又翻了翻logback与新版tomcat的源码&#xff0c;额…&#xff0c;日志架构原来也没那么神秘。本篇文章先过一遍原书内容…

CSS选择器(基本+复合+伪类)

目录 CSS选择器 基本选择器 标签选择器&#xff1a;使用标签名作为选择器->选中同名标签设置样式 类选择器&#xff1a;给类选择器定义一个名字.类名&#xff0c;并给标签添加class"类名" id选择器&#xff1a;跟类选择器非常相似&#xff0c;给id选择器定义…

Vitis HLS 学习笔记--理解串流Stream(1)

目录 1. 介绍 2. 示例 2.1 代码解析 2.2 串流数据类型 2.3 综合报告 3. 总结 1. 介绍 在Vitis HLS中&#xff0c;hls::stream是一个用于在C/C中进行高级合成的关键数据结构。它类似于C标准库中的std::stream&#xff0c;但是专门设计用于硬件描述语言&#xff08;如Veri…

windows vscode设置扩展和缓存目录

vscode的扩展和缓存占了很大的空间&#xff0c;而且默认在C盘&#xff0c;很烦。。。 修改vscode快捷方式的目标处&#xff1a;"C:\Users\Nv9\AppData\Local\Programs\Microsoft VS Code\Code.exe" --extensions-dir "D:\Program Cache\VScode\extensions"…

基于参数化建模的3D产品组态实现

我们最近为荷兰设计师家具制造商 KILO 发布了基于网络的 3D 配置器的第一个生产版本。我们使用了 Salsita 3D 配置器&#xff0c;这是一个内部 SDK&#xff0c;使新的 3D 配置器的实施变得轻而易举。虽然它给我们带来了巨大帮助&#xff0c;但我们仍然面临一些有趣的挑战。 NSD…

泰迪智能科技中职大数据实验室建设(职业院校大数据实验室建设指南)

职校大数据实验室是职校校园文化建设的重要部分&#xff0c;大数据实训室的建设方案应涵盖多个方面&#xff0c;包括硬件设施的配备、软件环境的搭建、课程资源的开发、师资力量的培养以及实践教学体系的完善等。 打造特色&#xff0c;对接生产 社会经济与产业的…

欧盟委员会发布《数据法》指南

文章目录 前言一、B to B和B to C的数据共享二、企业间数据共享三、不公平合同条款四、企业对政府的数据共享五、数据处理服务之间的切换六、关于第三国政府非法访问数据七、关于可互操作性八、关于《数据法》的执行前言 4月21日,欧盟委员会在其官方网站发布了《数据法》(Th…

论文阅读-THE GENERALIZATION GAP IN OFFLINE REINFORCEMENT LEARNING(ICLR 2024)

1.Motivation 本文希望比较online RL、offline RL、序列决策和BC等方法的泛化能力(对于不同的初始状态、transition functions、reward functions&#xff0c;现阶段offline RL训练的方式都是在同一个环境下的数据集进行训练)。实验发现offline的算法相较于online算法对新环境…

【Ping32】-企业级加密软件,让核心机密更安全!

Ping32&#xff0c;作为一款企业级加密软件&#xff0c;以其卓越的性能和强大的功能&#xff0c;致力于保护企业的核心机密安全。在当今这个信息化时代&#xff0c;企业的机密信息往往成为不法分子觊觎的目标&#xff0c;因此&#xff0c;如何确保核心机密的安全成为每个企业都…

【零基础】system generator①设置卡解析

1.在matlab中我们输入的是双精度浮点型数据&#xff0c;经过gateway后变成定点型。十六位十四个小数位&#xff0c;整个数据有十六位&#xff0c;其中十四位给了小数 2.fixed-point定点型&#xff1b;signed有符号&#xff1b;2’s comp补码 3.量化误差 truncate&#xff0c;舍…