C++函数模板详解(结合代码)
目录
1. 模板概念
2. 函数模板语法
3. 函数模板注意事项
4. 函数模板案例
5. 普通函数与函数模板的区别
6. 普通函数与函数模板的调用规则
7. 模板的局限性
1. 模板概念
在C++中,模板是一种通用的程序设计工具,它允许我们处理多种数据类型而不是固定的一种。函数模板就是其中之一,它使得我们可以编写一个函数来处理不同类型的数据。
模板的特点:
-
模板不可以直接使用,它只是一个框架,必须确定出T的类型(第3章有讲)
-
模板的通用并不是万能的
2. 函数模板语法
C++提供两种模板机制:函数模板和类模板 。为避免文章冗长,本文先介绍函数模板,下一篇文章介绍类模板。
函数模板作用:
建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。
使用语法:
template<typename T> 或者 template<class T>
template<typename T>表示声明一个模板,typename T是模板参数,
typename —— 表面其后面的符号是一种数据类型,可以用class代替。
个人习惯用template<typename T>代表函数模板,template<class T>代表类模板。
T —— 通用的数据类型,名称可以替换,通常为大写字母T。
示例:
//交换整型函数
void swapInt(int& a, int& b) {int temp = a;a = b;b = temp;
}//交换浮点型函数
void swapDouble(double& a, double& b) {double temp = a;a = b;b = temp;
}//利用模板提供通用的交换函数
template<typename T>
void mySwap(T& a, T& b)
{T temp = a;a = b;b = temp;
}void test01()
{int a = 10;int b = 20;//swapInt(a, b);//利用模板实现交换//1、自动类型推导mySwap(a, b);//2、显示指定类型mySwap<int>(a, b);cout << "a = " << a << endl;cout << "b = " << b << endl;}int main() {test01();system("pause");return 0;
}
-
函数模板利用关键字 template
-
使用函数模板有两种方式:1、自动类型推导 2、显示指定类型
-
模板的目的是为了提高复用性,将类型参数化
3. 函数模板注意事项
- 自动类型推导的限制:当使用自动类型推导时,编译器需要根据函数参数类型推导出一致的数据类型
T才能成功实例化模板函数。如果无法推导出一致的类型,将导致编译错误。// 示例代码 template<typename T> void myFunction(T arg1, T arg2) {// 函数体 }int main() {myFunction(10, 20); // 正确:推导出一致的 int 类型myFunction(10, 20.5); // 错误:无法推导出一致的类型return 0; }
- 模板参数的确定:在模板函数调用时,必须要确定模板参数的数据类型,否则编译器无法生成对应的函数实例。
// 2、模板必须要确定出T的数据类型,才可以使用 template<class T> void func() {cout << "func 调用" << endl; }void test01() {//func(); //错误,模板不能独立使用,必须确定出T的类型func<int>(); //利用显示指定类型的方式,给T一个类型,才可以使用该模板 }
fun<int>()代表对模板函数 fun 进行实例化,并指定模板参数 T 的具体类型为 int。在这种情况下,编译器会生成一个针对 T 为 int 类型的具体函数实现。其效果就好比是将模板中的 T 替换为 int,然后使用 int 类型的函数来处理相应的逻辑。
4. 函数模板案例
案例描述:
-
利用函数模板封装一个排序的函数,可以对不同数据类型数组进行排序
-
排序规则从大到小,排序算法为选择排序
-
分别利用char数组和int数组进行测试
//交换的函数模板
template<typename T>
void mySwap(T &a, T&b)
{T temp = a;a = b;b = temp;
}template<typename T> // 也可以替换成class
//利用选择排序,进行对数组从大到小的排序
void mySort(T arr[], int len)
{for (int i = 0; i < len; i++){int max = i; //最大数的下标for (int j = i + 1; j < len; j++){if (arr[max] < arr[j]){max = j;}}if (max != i) //如果最大数的下标不是i,交换两者{mySwap(arr[max], arr[i]);}}
}
template<typename T>
void printArray(T arr[], int len) {for (int i = 0; i < len; i++) {cout << arr[i] << " ";}cout << endl;
}
void test01()
{//测试char数组char charArr[] = "bdcfeagh";int num = sizeof(charArr) / sizeof(char);mySort(charArr, num);printArray(charArr, num);
}void test02()
{//测试int数组int intArr[] = { 7, 5, 8, 1, 3, 9, 2, 4, 6 };int num = sizeof(intArr) / sizeof(int);mySort(intArr, num);printArray(intArr, num);
}int main() {test01();test02();system("pause");return 0;
}

5. 普通函数与函数模板的区别
普通函数与函数模板区别:
-
普通函数调用时可以发生自动类型转换(隐式类型转换)
-
函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
-
如果利用显示指定类型的方式,可以发生隐式类型转换
示例:
// 普通函数和函数模板的区别// 1、普通函数调用可以发生隐式类型转换
// 2、函数模板用 自动类型推导时,不可以发生隐式类型转换
// 3、函数模板用 显示指定类型时, 可以发生隐式类型转换// 普通函数
int myAdd01(int a, int b)
{return a + b;
}// 模板函数
template<typename T>
T myAdd02(T a, T b)
{return a + b;
}void test01()
{int a = 10;int b = 20;char c = 'c';cout << "普通函数:int a + int b = " << myAdd01(a, b) << endl;// 普通函数中,隐式的将c转成了ASSIC码,c-99cout << "普通函数:int a + char c = " << myAdd01(a, c) << endl;// 自动类型推导// 会报错,模板函数自动类型推导时,不会发生隐式类型转换// cout << "模板函数:int a + char c = " << myAdd02(a, c) << endl;// 显示指定类型cout << "模板函数:int a + char c = " << myAdd02<int>(a, c) << endl;
}int main()
{test01();
}

6. 普通函数与函数模板的调用规则
调用规则如下:
-
如果函数模板和普通函数都可以实现,优先调用普通函数
-
可以通过空模板参数列表来强制调用函数模板
-
函数模板也可以发生重载
-
如果函数模板可以产生更好的匹配,优先调用函数模板
//普通函数与函数模板调用规则
void myPrint(int a, int b)
{cout << "调用的普通函数" << endl;
}template<typename T>
void myPrint(T a, T b)
{ cout << "调用的模板" << endl;
}template<typename T>
void myPrint(T a, T b, T c)
{ cout << "调用重载的模板" << endl;
}void test01()
{//1、如果函数模板和普通函数都可以实现,优先调用普通函数// 注意 如果告诉编译器 普通函数是有的,但只是声明没有实现,或者不在当前文件内实现,就会报错找不到int a = 10;int b = 20;myPrint(a, b); //调用普通函数//2、可以通过空模板参数列表来强制调用函数模板myPrint<>(a, b); //调用函数模板//3、函数模板也可以发生重载int c = 30;myPrint(a, b, c); //调用重载的函数模板//4、 如果函数模板可以产生更好的匹配,优先调用函数模板char c1 = 'a';char c2 = 'b';myPrint(c1, c2); //调用函数模板
}int main()
{test01();system("pause");return 0;
}

既然提供了函数模板,最好就不要提供普通函数,否则容易出现二义性。
7. 模板的局限性
例如:
template<class T>void f(T a, T b){ a = b;}
如果数据类型是int型,此时的赋值操作没问题。如果传入的a和b是一个数组,就无法实现了。
再例如:
template<class T>void f(T a, T b){ if(a > b) { ... }}
如果T的数据类型传入的是像Person这样的自定义数据类型,也无法正常运行。
因此C++为了解决这种问题,提供模板的重载,可以为这些特定的类型提供具体化的模板
#include<iostream>
using namespace std;#include <string>class Person
{
public:Person(string name, int age){this->m_Name = name;this->m_Age = age;}string m_Name;int m_Age;
};//普通函数模板
template<class T>
bool myCompare(T& a, T& b)
{if (a == b){return true;}else{return false;}
}//具体化,显示具体化的原型template<>开头,并通过名称来指出类型
//具体化优先于常规模板
template<> bool myCompare(Person &p1, Person &p2)
{if ( p1.m_Name == p2.m_Name && p1.m_Age == p2.m_Age){return true;}else{return false;}
}void test01()
{int a = 10;int b = 20;//内置数据类型可以直接使用通用的函数模板bool ret = myCompare(a, b);if (ret){cout << "a == b " << endl;}else{cout << "a != b " << endl;}
}void test02()
{Person p1("Tom", 10);Person p2("Tom", 10);//自定义数据类型,不会调用普通的函数模板//可以创建具体化的Person数据类型的模板,用于特殊处理这个类型bool ret = myCompare(p1, p2);if (ret){cout << "p1 == p2 " << endl;}else{cout << "p1 != p2 " << endl;}
}int main()
{test01();test02();system("pause");return 0;
}

在这个特定的例子中,通用的函数模板 myCompare 用于比较两个对象是否相等,但是当对象类型是 Person 类型时,我们想要做一些特殊的比较,比如比较 Person 类的 m_Name 和 m_Age 成员变量。为了实现这一目的,我们对通用模板进行了具体化。
具体化的语法是在 template<> 关键字后面指定特定的类型,这里是 Person,然后是模板的原型,即函数签名 bool myCompare(Person &p1, Person &p2)。在函数体中,我们以特定方式实现了 Person 类型对象的比较逻辑,即比较它们的名字和年龄。
当在代码中调用 myCompare 函数,并传递 Person 类型的参数时,编译器会优先选择这个具体化版本,而不是通用的模板版本。
总结:
-
利用具体化的模板,可以解决自定义类型的通用化
-
学习模板并不是为了写模板,而是在STL能够运用系统提供的模板
相关文章:
C++函数模板详解(结合代码)
目录 1. 模板概念 2. 函数模板语法 3. 函数模板注意事项 4. 函数模板案例 5. 普通函数与函数模板的区别 6. 普通函数与函数模板的调用规则 7. 模板的局限性 1. 模板概念 在C中,模板是一种通用的程序设计工具,它允许我们处理多种数据类型而不是固…...
Nest学习随笔
一、Middleware(中间件)、Interceptor(拦截器)、ExceptionFilter(异常过滤器) 执行顺序 接口调用正常:Middleware > Interceptor接口调用异常:Middleware > ExceptionFilter 二、访问静态文件 使用 nestjs/serve-static 依赖 配置方法&#x…...
二十二、软考-系统架构设计师笔记-真题解析-2018年真题
软考-系统架构设计师-2018年上午选择题真题 考试时间 8:30 ~ 11:00 150分钟 1.在磁盘调度管理中,应先进行移臂调度,再进行旋转调度。假设磁盘移动臂位于21号柱面上,进程的请求序列如下表所示。如果采用最短移臂调度算法,那么系统…...
2024最新最全Selenium自动化测试面试题!
1、什么是自动化测试、自动化测试的优势是什么? 通过工具或脚本代替手工测试执行过程的测试都叫自动化测试。 自动化测试的优势: 1、减少回归测试成本 2、减少兼容性测试成本 3、提高测试反馈速度 4、提高测试覆盖率 5、让测试工程师做更有意义的…...
Docker 搭建Redis集群
目录 1. 3主3从架构说明 2. 3主3从Redis集群配置 2.1关闭防火墙启动docker后台服务 2.2 新建6个docker容器实例 2.3 进去任意一台redis容器,为6台机器构建集群关系 2.4 进去6381,查看集群状态 3. 主从容错切换迁移 3.1 数据读写存储 3.1.1 查看…...
spring boot商城、商城源码 欢迎交流
一个基于spring boot、spring oauth2.0、mybatis、redis的轻量级、前后端分离、防范xss攻击、拥有分布式锁,为生产环境多实例完全准备,数据库为b2b2c设计,拥有完整sku和下单流程的商城 联系: V-Tavendor...
全面解析“通义千问”:功能、优势与使用指南
引言: “通义千问”是由阿里云研发的一款先进的人工智能语言模型,以其强大的自然语言处理能力与广泛的知识覆盖面,在教育、咨询、信息检索等领域发挥着重要作用。本文将详细介绍“通义千问”的核心功能、显著优势以及具体使用方法。 一、“…...
【第三方登录】Google邮箱
登录谷歌邮箱开发者 https://console.developers.google.com/ 先创建项目 我们用的web应用 设置回调 核心主要: 1.创建应用 2.创建客户端ID 3.设置域名和重定向URL 4.对外公开,这样所有的gmail邮箱 都能参与测试PHP代码实现 引入第三方包 h…...
oslo_config学习小结
2.配置文件加载方法 2.1基础 配置文件指的是文件以.conf,.ini结尾等内容为配置项的文件,配置文件内容格式一般为 [DEFAULT] option value [sectiona] optiona valuea optionb valueb [sectionb] optionc valuec optiond valued 2.2加载方法…...
SpringBoot2.6.3 + knife4j-openapi3
1.引入项目依赖: <dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-spring-boot-starter</artifactId><version>4.5.0</version> </dependency> 2.新增配置文件 import io.swag…...
PostgreSQL FDW(外部表) 简介
1、FDW: 外部表 背景 提供外部数据源的透明访问机制。PostgreSQL fdw(Foreign Data Wrapper)是一种外部访问接口,可以在PG数据库中创建外部表,用户访问的时候与访问本地表的方法一样,支持增删改查。 而数据则是存储在外部,外部可以是一个远程的pg数据库或者其他数据库(…...
Java项目:75 springboot房产销售系统
作者主页:源码空间codegym 简介:Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 使用房产销售系统分为管理员和用户、销售经理三个角色的权限子模块。 管理员所能使用的功能主要有:首页、个人中心、用户管理、销…...
2.6 IDE(集成开发环境)是什么
IDE(集成开发环境)是什么 IDE 是 Integrated Development Environment 的缩写,中文称为集成开发环境,用来表示辅助程序员开发的应用软件,是它们的一个总称。 通过前面章节的学习我们知道,运行 C 语言&…...
tomcat和web服务器是什么??
一、什么是服务器 1.服务器是计算机的一种,它比普通计算机运行更快、负载更高。服务器拥有独立IP地址,并且运行了服务器软件。 2.服务器由服务器软件和服务器硬件组成。服务器硬件就是拥有独立ip的计算机,服务器软件是一个被动的软件&#…...
鸿蒙Harmony跨模块交互
1. 模块分类介绍 鸿蒙系统的模块一共分为四种,包括HAP两种和共享包两种 HAP(Harmony Ability Package) Entry:项目的入口模块,每个项目都有且只有一个。feature:项目的功能模块,内部模式和En…...
由浅到深认识Java语言(30):集合
该文章Github地址:https://github.com/AntonyCheng/java-notes 在此介绍一下作者开源的SpringBoot项目初始化模板(Github仓库地址:https://github.com/AntonyCheng/spring-boot-init-template & CSDN文章地址:https://blog.c…...
Python学习笔记(二)
一:异常: 1.1:异常处理: 1.2:异常捕获: 1.3:异常传递: 二:模块: 2.1:模块的定义: 2.2:模块的导入: 2.3&…...
5.域控服务器都要备份哪些资料?如何备份DNS服务器?如何备份DHCP服务器?如何备份组策略?如何备份服务器状态的备份?
(2.1) NTD(域控数据库)备份 (2.2)DNS备份 (2.3)DHCP备份 (2.4)组策略备份 (2.5)CA证书备份 (2.6)系统状态备份 (2.1)…...
TCP与UDP:网络协议的技术原理与要点
文章目录 1. TCP(传输控制协议)1.1 面向连接1.1.1 三次握手1.1.2 为什么需要三次握手?1.1.3 四次挥手1.1.4 为什么需要四次挥手? 1.2 可靠性1.3 有序传输1.4 流量控制1.5 拥塞控制 2. UDP(用户数据报协议)2…...
vue-office/docx插件实现docx文件预览
1.下包 //预览docx文件 npm install vue-office/docx vue-demi//如果是vue2.6版本或以下还需要额外安装 vue/composition-api2.引入 <template><div>//在src填入文档地址<VueOfficeDocx srchttp://...../xx.docx style"width:80%" rendered"re…...
AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...
练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...
YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...
