c++泛型编程与模板-01函数模板
函数模板的定义
所谓函数模板,实际就是写一个通用函数,返回值和参数的类型都是可变的,用一个特定格式的变量来指定,然后调用此函数的时候,编译器会根据参数的数据类型自动推导出类型,从而达到函数再不同的类型参数下可复用的目的。这个通用函数就是函数模板,使用模板就无须关心数据类型,只定义一个函数模板就可以。
定义函数模板的语法格式如下所示:
template<typename 类型占位符>
返回值类型 函数名(参数列表)
{//函数体;
}
其中:template 是声明模板的关键字,<>中的参数称为模板参数;typename 关键字用于标识模板参数,可以用class关键字代替,class 和 typename 并没有区别。模板参数不能为空,一个函数模板中可以有多个模板参数,模板参数和普通函数参数相似。template下面是定义的函数模板,函数模板定义方式与普通函数定义方式相同,只是参数列表中的数据类型要使用<>中的参数名表示。
函数模板实例化
函数模板并不是一个函数,它相当于一个模子,定义一次即可使用不同类型的参数来调用该函数模板,这样做可以减少代码的书写,提高代码的复用性和效率。函数模板不会减少可执行程序的大小,因为编译器会根据调用时的参数类型进行相应的实例化。所谓实例化,就是用类型参数替换模板中的模板参数,生成具体类型的函数。实例化可分为隐式实例化与显式实例化。
Example01
#include <iostream>// 定义函数模板
template <typename T>
T my_add(T t1, T t2)
{return t1 + t2;
}int main(int argc, char** argv)
{std::cout << my_add(1, 2) << std::endl; // 传入int类型参数std::cout << my_add(1.0, 2.0) << std::endl; // 传入double类型参数return 0;
}
隐式实例化
隐式实例化是根据函数调用时传入的参数的数据类型确定模板参数 T 的类型,模板参数的类型是隐式确定的.
在Example01中第一次调用my_add()函数模板时传入的是int类型的数据1、2,编译器根据传入的实参推演出模板参数类型是int,就会根据函数模板实例化出一个int类型的函数,如下所示:
int my_add(int t1, int t2)
{return t1 + t2;
}
编译器生成具体类型函数的这一过程就称为实例化,生成的函数称为模板函数。生成int类型的函数后,再传入实参1和2进行运算。
同理,当传入 double 类型的数据时,编译器先根据模板实例化出如下形式的函数:
double my_add(double t1, double t2)
{return t1 + t2;
}
这样,每一次调用时都会根据不同的类型实例化出不同类型的函数,最终的可执行程序的大小并不会减少,只是提高了代码的复用性。
显示实例化
隐式实例化不能为同一个模板参数指定两种不同的类型,这就需要显式实例化解决类型不一致的问题。显式实例化需要指定函数模板中的数据类型,语法格式如下所示:
template函数返回值类型 函数名<实例化的类型>(参数列表);
在Example01中,显示实例化为 int 类型,则在调用时,不是 int 类型的数据会转换为int类型再进行计算.
#include <iostream>// 定义函数模板
template <typename T>
T my_add(T t1, T t2)
{return t1 + t2;
}template int my_add<int>(int t1, int t2); // 显示实例化为int类型int main(int argc, char** argv)
{// 函数模板调用// 在调用int类型函数模板时,传入一个字符'a',则编译器会将字符类型'a'转换为ASCII码值,然后在与1相加得出结果std::cout << my_add<int>(1, 'a') << std::endl; std::cout << my_add(1.0, 2.0) << std::endl;return 0;
}
需要注意的是,对于给定的函数模板,显式实例化声明在一个文件中只能出现一次,并且在这个文件中必须给出函数模板的定义。
显示具体化
函数模板的显式具体化是对函数模板的重新定义,具体格式如下所示:
template<> 函数返回值类型 函数名<实例化类型>(参数列表)
{//body
}
Example02
定义交换两个数据的函数模板,示例代码如下:
template<typename T>
void my_swap(T &t1, T &t2)
{T temp = t1;t1 = t2;t2 = temp;
}
但现在有如下结构体定义,示例代码如下:
struct Student
{uint64_t id;float score;char name[40];
};
现在想交换2个学生的id,但是又不想交换姓名、成绩等其他信息,则可以用显示具体化方式重新定义函数模板,只需交换结构体的部分成员变量即可。则该显示具体化如下所示:
template <>
void my_swap<Student>(Student& t1, Student& t2)
{int temp = t1.id;t1.id = t2.id;t2.id = temp;
}
如果函数有多个原型,则编译器在选择函数调用时,非模板函数优先于模板函数,显式具体化模板优先于函数模板.例如下面三种定义:
void my_swap(int&, int&); //直接定义
template<typename T>void my_swap(T& t1, T& t2); //模板定义
template<> void my_swap<int>(int&, int&); //显式具体化
对于int a,int b,在调用my_swap(a, b)时,则**优先调用直接定义的函数;如果没有则优先调用显示具体化,如果2者都没有才会调用函数模板。
函数模板重载
函数模板可以进行实例化,以支持不同类型的参数,不同类型的参数调用会产生一系列重载函数。
在Example01中两次调用my_add() 函数模板,编译器会根据传入参数不同实例化出两个函数,如下所示:
int my_add(int t1, int t2) // int 类型参数实例化出的函数
{return t1 + t2;
}double my_add(double t1, double t2)// double 类型参数实例化出的函数
{return t1 + t2;
}
函数模板本身也可以被重载,即名称相同的函数模板可以具有不同的函数模板定义,当进行函数调用时,编译器根据实参的类型与个数决定调用哪个函数模板实例化函数。
#include <iostream>
int my_max(const int& a, const int& b) // 非模板函数,求两个int类型数据的较大值
{return a > b ? a : b;
}template <typename T> // 定义求两个任意类型数据的较大值
T my_max(const T& t1, const T& t2)
{return t1 > t2 ? t1 : t2;
}template <typename T> // 定义求三个任意类型数据的最大值
T my_max(const T& t1, const T& t2, const T& t3)
{return my_max(my_max(t1, t2), t3);
}int main(int argc, char** argv)
{std::cout << my_max(1, 2) << std::endl; // 调用非模板函数std::cout << my_max(1, 2, 3) << std::endl; // 调用三个参数的函数模板std::cout << my_max('a', 'e') << std::endl; // 调用两个参数的函数模板std::cout << my_max(6, 3.2) << std::endl; // 调用非模板函数return 0;
}
注意,模板不允许自动类型转化,如果有不同类型参数,只允许使用非模板函数,因为普通函数可以进行自动类型转换。
函数模板使用的注意项
- <>中的每一个类型参数在函数模板参数列表中必须至少使用一次
template<typename T1, typename T2> void func(T1 t) { }
- 全局作用域中声明的与模板参数同名的对象、函数或类型,在函数模板中将被隐藏
int num; template<typename T> void func(T t) {T num;cout<<num<<endl; //输出的是局部变量num,全局int类型的num被屏蔽 }
- 函数模板中声明的对象或类型不能与模板参数同名
template<typename T>void func(T t){typedef float T; //错误,定义的类型与模板参数名相同}
- 模板参数名在同一模板参数列表中只能使用一次,但可在多个函数模板声明或定义之间重复使用
template<typename T, typename T> //错误,在同一个模板中重复定义模板参数void func1(T t1, T t2){}template<typename T>void func2(T t1){}template<typename T> //在不同函数模板中可重复使用相同的模板参数名void func3(T t1){}
- 模板的定义和多处声明所使用的模板参数名不是必须相同
//模板的前向声明 template<typename T> void func1(T t1, T t2);//模板的定义 template<typename U> void func1(U t1, U t2) { }
- 如果函数模板有多个模板参数,则每个模板参数前都必须使用关键字 class 或 typename 修饰
template<typename T, typename U> //两个关键字可以混用 void func(T t, U u){}template<typename T,U> //错误,每一个模板参数前都必须有关键字修饰 void func(T t, U u){}
相关文章:
c++泛型编程与模板-01函数模板
函数模板的定义 所谓函数模板,实际就是写一个通用函数,返回值和参数的类型都是可变的,用一个特定格式的变量来指定,然后调用此函数的时候,编译器会根据参数的数据类型自动推导出类型,从而达到函数再不同的…...

Golang http请求忘记调用resp.Body.Close()而导致的协程泄漏问题(含面试常见协程泄漏相关测试题)
参考: 知乎:别因为忘记close你的httpclient,造成goroutine泄漏 CSDN:resp.Body.Close() 引发的内存泄漏goroutine个数 先来看几道题,想一想最终的输出结果是多少呢? package mainimport ("fmt"…...

进程信号生命周期详解
信号和信号量半毛钱关系都没有! 每个信号都有一个编号和一个宏定义名称,这些宏定义可以在signal.h中找到,例如其中有定 义 #define SIGINT 2 查看信号的机制,如默认处理动作man 7 signal SIGINT的默认处理动作是终止进程,SIGQUIT的默认处理…...

2023-03-03干活小计
今天见识了 归一化的重要性:归一化 不容易爆炸 深度了解了学习率:其实很多操作 最后的结果都是改变了lr 以房价预测为例:一个点一个点更新 比较 矩阵的更新: 为什么小批量梯度下降 优于随机梯度下降 优于批量梯度下降ÿ…...
操作系统结构
随着操作系统的不断增多和代码规模的不断扩大,提供合理的结构对提升操作系统的安全与可靠性来说变得尤为重要。 1.分层法 指将操作系统分为若干层,最低层位硬件,最高层为用户接口,每层只能调用紧邻它的低层的功能和服务(类似于计…...

[SSD科普] 固态硬盘物理接口SATA、M.2、PCIe常见疑问,如何选择?
前言犹记得当年Windows 7系统体验指数中,那5.9分磁盘分数,在其余四项的7.9分面前,似乎已经告诉我们机械硬盘注定被时代淘汰。势如破竹的SSD固态硬盘,彻底打破了温彻斯特结构的机械硬盘多年来在电脑硬件领域的统治。SSD数倍于HDD机…...

【Java学习笔记】3.Java 基础语法
Java 基础语法 一个 Java 程序可以认为是一系列对象的集合,而这些对象通过调用彼此的方法来协同工作。下面简要介绍下类、对象、方法和实例变量的概念。 对象:对象是类的一个实例,有状态和行为。例如,一条狗是一个对象ÿ…...
Python基础学习6——if语句
基本概念 if语句为条件判断语句,用来判断if后面的语句是真是假。if的用途有很多,比如作为条件测试可以判断两数是否相等与不等、进行数值笔记等等。例子如下: Lego_price (599, 799, 898) if Lego_price[0] 599:print("Correct!&quo…...

有免费的PDF转Word吗?值得收藏的7个免费 PDF转Word工具请收好
PDF 和 DOC 是人们在工作中广泛使用的两种最流行的文档格式。PDF 是 Adobe 的便携式文档格式,DOC 是 Microsoft 的 Word 文档格式。PDF 是一种更安全可靠的文件格式,因为它很难编辑 PDF 文件,但是有一些称为 PDF 编辑器的工具可用于编辑 PDF …...

Thinkphp6使用RabbitMQ消息队列
Thinkphp6连接使用RabbitMQ(不止tp6,其他框架对应改下也一样),如何使用Docker部署RabbitMQ,在上一篇已经讲了->传送门<-。 部署环境 开始前先进入RabbitMQ的web管理界面,选择Queues菜单,点…...

小成本互联网创业怎么做?低成本创业的方法分享
多数人都会有想法创业,尤其是在互联网上面创业,很多人看到了商机,但是因为成本的原因又放弃了,实际上,小成本也可以互联网创业!那么,小成本互联网创业怎么做?低成本创业的方法在这里…...
六、栈、栈的相关问题
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 前言 一、栈 1.栈概述 2.栈的实现 2.1 栈的API 2.2 栈的实现 二、栈的括号匹配问题 1.问题描述 2.代码实现 三、逆波兰表达式求值问题 1.问题描述 2.代码 总结 前言 提…...
Java安全停止线程
Thread 类虽提供了一个 stop() 方法(已经被废弃),但由于 stop() 方法强制终止一个正在执行的线程,可能会造成数据不一致的问题,所以在生产环境中最好不要使用。 场景: 由于一些操作需要轮询处理ÿ…...

12 readdir 函数
前言 在之前 ls 命令 中我们可以看到, ls 命令的执行也是依赖于 opendir, readdir, stat, lstat 等相关操作系统提供的相关系统调用来处理业务 因此 我们这里来进一步看一下 更细节的这些 系统调用 我们这里关注的是 readdir 这个函数, 入口系统调用是 getdents 如下调试…...

Windows环境搭建Android开发环境-Android Studio/Git/JDK
Windows环境搭建Android开发环境-Android Studio/Git/JDK 因为休假回来后公司的开发环境由Ubuntu变为了Windows,所以需要重新配置一下开发环境。 工作多年第一次使用Windows环境进行开发工作,作次记录下来。 一、 Git安装 1.1git 标题软件下载 网址&…...

全国爱耳日丨听力受损严重有哪些解决办法
——【科学爱耳护耳,实现主动健康】随着数码电子设备使用越来越方便、日常使用时间越来越长,听力障碍、患上耳道疾病一系列问题也接踵而至,在当下我们必须重视听力健康,采取更科学的听音方式,保护听力健康,…...

【抽水蓄能电站】基于粒子群优化算法的抽水蓄能电站的最佳调度方案研究(Matlab代码实现)
👨🎓个人主页:研学社的博客💥💥💞💞欢迎来到本博客❤️❤️💥💥🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密…...

【异常】因多租户字段缺少导致Error updating database. Column ‘tenant_id‘ cannot be null
一、报错内容 org.springframework.dao.DataIntegrityViolationException: ### Error updating database. Cause: java.sql.SQLIntegrityConstraintViolationException: Column tenant_id cannot be null ### The error may exist in com/xxx/cloud/mall/admin/mapper/Goods…...

类和对象(上)
文章目录 面向对象的初步认知类的实例化this引用对象的构造及初始化封装static成员代码块内部类 对象的打印一、面向对象的初步认知 Java是一门纯面向对象的语言(Object Oriented Program,简称OOP),在面向对象的世界里,一切皆为对象。在java中…...

Java经典面试题——谈谈 Java 反射机制,动态代理是基于什么原理?
典型回答 反射机制是 Java 语言提供的一种基本功能,赋予程序在运行时 自省(introspect,官方用语)的能力。通过反射我们可以直接操作类或者对象,比如获取某个对象的类定义,获取类声明的属性和方法ÿ…...

docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...

MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...

STM32F4基本定时器使用和原理详解
STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...

【论文阅读28】-CNN-BiLSTM-Attention-(2024)
本文把滑坡位移序列拆开、筛优质因子,再用 CNN-BiLSTM-Attention 来动态预测每个子序列,最后重构出总位移,预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵(S…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...
《C++ 模板》
目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板,就像一个模具,里面可以将不同类型的材料做成一个形状,其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式:templa…...

MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...