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

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函数模板

函数模板的定义 所谓函数模板&#xff0c;实际就是写一个通用函数&#xff0c;返回值和参数的类型都是可变的&#xff0c;用一个特定格式的变量来指定&#xff0c;然后调用此函数的时候&#xff0c;编译器会根据参数的数据类型自动推导出类型&#xff0c;从而达到函数再不同的…...

Golang http请求忘记调用resp.Body.Close()而导致的协程泄漏问题(含面试常见协程泄漏相关测试题)

参考&#xff1a; 知乎&#xff1a;别因为忘记close你的httpclient&#xff0c;造成goroutine泄漏 CSDN&#xff1a;resp.Body.Close() 引发的内存泄漏goroutine个数 先来看几道题&#xff0c;想一想最终的输出结果是多少呢&#xff1f; package mainimport ("fmt"…...

进程信号生命周期详解

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

2023-03-03干活小计

今天见识了 归一化的重要性&#xff1a;归一化 不容易爆炸 深度了解了学习率&#xff1a;其实很多操作 最后的结果都是改变了lr 以房价预测为例&#xff1a;一个点一个点更新 比较 矩阵的更新&#xff1a; 为什么小批量梯度下降 优于随机梯度下降 优于批量梯度下降&#xff…...

操作系统结构

随着操作系统的不断增多和代码规模的不断扩大&#xff0c;提供合理的结构对提升操作系统的安全与可靠性来说变得尤为重要。 1.分层法 指将操作系统分为若干层&#xff0c;最低层位硬件&#xff0c;最高层为用户接口&#xff0c;每层只能调用紧邻它的低层的功能和服务(类似于计…...

[SSD科普] 固态硬盘物理接口SATA、M.2、PCIe常见疑问,如何选择?

前言犹记得当年Windows 7系统体验指数中&#xff0c;那5.9分磁盘分数&#xff0c;在其余四项的7.9分面前&#xff0c;似乎已经告诉我们机械硬盘注定被时代淘汰。势如破竹的SSD固态硬盘&#xff0c;彻底打破了温彻斯特结构的机械硬盘多年来在电脑硬件领域的统治。SSD数倍于HDD机…...

【Java学习笔记】3.Java 基础语法

Java 基础语法 一个 Java 程序可以认为是一系列对象的集合&#xff0c;而这些对象通过调用彼此的方法来协同工作。下面简要介绍下类、对象、方法和实例变量的概念。 对象&#xff1a;对象是类的一个实例&#xff0c;有状态和行为。例如&#xff0c;一条狗是一个对象&#xff…...

Python基础学习6——if语句

基本概念 if语句为条件判断语句&#xff0c;用来判断if后面的语句是真是假。if的用途有很多&#xff0c;比如作为条件测试可以判断两数是否相等与不等、进行数值笔记等等。例子如下&#xff1a; Lego_price (599, 799, 898) if Lego_price[0] 599:print("Correct!&quo…...

有免费的PDF转Word吗?值得收藏的7个免费 PDF转Word工具请收好

PDF 和 DOC 是人们在工作中广泛使用的两种最流行的文档格式。PDF 是 Adobe 的便携式文档格式&#xff0c;DOC 是 Microsoft 的 Word 文档格式。PDF 是一种更安全可靠的文件格式&#xff0c;因为它很难编辑 PDF 文件&#xff0c;但是有一些称为 PDF 编辑器的工具可用于编辑 PDF …...

Thinkphp6使用RabbitMQ消息队列

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

小成本互联网创业怎么做?低成本创业的方法分享

多数人都会有想法创业&#xff0c;尤其是在互联网上面创业&#xff0c;很多人看到了商机&#xff0c;但是因为成本的原因又放弃了&#xff0c;实际上&#xff0c;小成本也可以互联网创业&#xff01;那么&#xff0c;小成本互联网创业怎么做&#xff1f;低成本创业的方法在这里…...

六、栈、栈的相关问题

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 一、栈 1.栈概述 2.栈的实现 2.1 栈的API 2.2 栈的实现 二、栈的括号匹配问题 1.问题描述 2.代码实现 三、逆波兰表达式求值问题 1.问题描述 2.代码 总结 前言 提…...

Java安全停止线程

Thread 类虽提供了一个 stop() 方法&#xff08;已经被废弃&#xff09;&#xff0c;但由于 stop() 方法强制终止一个正在执行的线程&#xff0c;可能会造成数据不一致的问题&#xff0c;所以在生产环境中最好不要使用。 场景&#xff1a; 由于一些操作需要轮询处理&#xff…...

12 readdir 函数

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

Windows环境搭建Android开发环境-Android Studio/Git/JDK

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

全国爱耳日丨听力受损严重有哪些解决办法

——【科学爱耳护耳&#xff0c;实现主动健康】随着数码电子设备使用越来越方便、日常使用时间越来越长&#xff0c;听力障碍、患上耳道疾病一系列问题也接踵而至&#xff0c;在当下我们必须重视听力健康&#xff0c;采取更科学的听音方式&#xff0c;保护听力健康&#xff0c;…...

【抽水蓄能电站】基于粒子群优化算法的抽水蓄能电站的最佳调度方案研究(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5;&#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密…...

【异常】因多租户字段缺少导致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&#xff0c;简称OOP)&#xff0c;在面向对象的世界里&#xff0c;一切皆为对象。在java中…...

Java经典面试题——谈谈 Java 反射机制,动态代理是基于什么原理?

典型回答 反射机制是 Java 语言提供的一种基本功能&#xff0c;赋予程序在运行时 自省&#xff08;introspect&#xff0c;官方用语&#xff09;的能力。通过反射我们可以直接操作类或者对象&#xff0c;比如获取某个对象的类定义&#xff0c;获取类声明的属性和方法&#xff…...

DAY 47

三、通道注意力 3.1 通道注意力的定义 # 新增&#xff1a;通道注意力模块&#xff08;SE模块&#xff09; class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

sqlserver 根据指定字符 解析拼接字符串

DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

图表类系列各种样式PPT模版分享

图标图表系列PPT模版&#xff0c;柱状图PPT模版&#xff0c;线状图PPT模版&#xff0c;折线图PPT模版&#xff0c;饼状图PPT模版&#xff0c;雷达图PPT模版&#xff0c;树状图PPT模版 图表类系列各种样式PPT模版分享&#xff1a;图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

Reasoning over Uncertain Text by Generative Large Language Models

https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...

uniapp手机号一键登录保姆级教程(包含前端和后端)

目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号&#xff08;第三种&#xff09;后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...

鸿蒙(HarmonyOS5)实现跳一跳小游戏

下面我将介绍如何使用鸿蒙的ArkUI框架&#xff0c;实现一个简单的跳一跳小游戏。 1. 项目结构 src/main/ets/ ├── MainAbility │ ├── pages │ │ ├── Index.ets // 主页面 │ │ └── GamePage.ets // 游戏页面 │ └── model │ …...

针对药品仓库的效期管理问题,如何利用WMS系统“破局”

案例&#xff1a; 某医药分销企业&#xff0c;主要经营各类药品的批发与零售。由于药品的特殊性&#xff0c;效期管理至关重要&#xff0c;但该企业一直面临效期问题的困扰。在未使用WMS系统之前&#xff0c;其药品入库、存储、出库等环节的效期管理主要依赖人工记录与检查。库…...