news 2026/4/15 10:34:15

函数模板.

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
函数模板.

闲谈:上一篇初识模板我也只是初略了解,这一次我在整理一下。

模板

模板是C++支持泛型程序设计的工具,通过它可以实现参数化多态性。

参数化多态性:就是将程序所处理的对象的类型参数化,使得一段程序可以处理多种不同类型的对象。

函数模板

与重载、宏定义对比所解决的缺陷

重载与函数模板

函数模板与函数重载看起来大同小异,但是重载的类不能传入与之相同类型的参数,从而使得要多建几个代码框架相同的函数,使程序代码冗余,可读性降低。

比如:

int max(int x,int y){…} float max(float x,float y){…}

与之相似的还有宏定义:

宏定义只是在编译时进行简单的宏展开,避开了类型检查机制,容易产生隐藏错误。

#define max(x,y) ((x)>(y)?(x):(y)) max(a++, b++) //替换后:((a++)>(b++)?(a++):(b++))

像这样,我们调用max宏定义替换时,a和b会产生多次自增,而我们需要的只是一次自增

函数模板

模板可以轻松地解决上述问题,减少隐式错误和减少代码冗余。

还是以max举个例子:

template <class T> T max(T x, T y){ return x > y ? x : y; }
函数模板的使用

函数模板的格式:

template <class 形参名1,class 形参名2,......>

返回类型 函数名(参数列表) {

函数体

}

以上面举例的max模板为例:

关键字template后面的尖括号表明,max函数要用到一个叫做T的参数(我们称作模板参数),而这个参数是一种类型。

该模板的含义就是无论参数T为int、char或其他数据类型(包括类类型),函数max的语意都是对x和y求最大值。

这样定义的max代表了一类具有相同程序逻辑的函数,称为函数模板。

注意:使用函数模板时一定要实例化,。

原因:函数模板本身是不被编译器编译的,必须使用实例化给模板T绑定类型后才能使用。

完整使用:

template <class T> T max(T x, T y){ return x > y ? x : y; } //一下是传入模板后编译器扩展结果 //double max(double x, double y) //{ return x > y ? x : y; } int main( ){ double a = 1.0, b2,b1; b1 = max(a, 2.0);//这里是隐式实例化b b2 = max<double>(a, 2.0)//显示实例化 return 0; }

直接使用max()传参识,函数模板接受了一个隐含的参数:double。

编译器自动将函数模板扩展成一个完整的关于double数据比较大小的函数,然后再在函数模板被调用的地方产生合适的函数调用代码。

显示调用就是我们手动转换了传参的类型。

这里有个问题了,既然要实例化才能使用模板,那这里我就需要传入类型不同的参数咋办啊?这里那就需要我们在模板里多写几条class T来分类传入类型了!

template <class T1, class T2> auto max(T1 x, T2 y) { return x > y ? x : y; } //一下是传入模板后编译器扩展结果 //传入res1: //double max(int x, double y) //{ return x > y ? x : y; } //传入res2: //double max(int x, float y) //{ return x > y ? x : y; } int main() { int a = 10; double b = 20.5; float c = 15.8f; auto res1 = max(a, b); cout << "int和double的最大值:" << res1 << endl; auto res2 = max(b, c); cout << "double和float的最大值:" << res2 << endl; return 0; }

当然我还是拿之前的例子举例,这里返回值会被强制全部转换成double返回。但想传入不同类型参数,大抵就是这样使用了。

函数模板传入类类型的使用

模板除了简单的传入类型,我们也可以将自定义类型传入进去,比如这样,我定义有个学生类来比较:

class Student { public: string name; int score; Student(string n, int s) : name(n), score(s) {} }; template <class T> T max(T x, T y) { return x > y ? x : y; } bool operator>(const Student& a, const Student& b) { return a.score > b.score; } int main() { Student s1("张三", 80); Student s2("李四", 90); Student s_max = max(s1, s2); cout << "成绩更高的学生:" << s_max.name << ",成绩:" << s_max.score << endl; return 0; }

当然了,这里的“>”肯定是比较不了我们自定义类型的,所以我们要对此进行一次运算符重载。

模板匹配策略

还有个问题,那就是如果出现了与函数模板相同的函数,那该怎么办?我怎么知道该调用谁?就比如下面这例子:

template <class T> T max(T x, T y){ return x > y ? x : y; } int max(int x, int y){ return x > y ? x : y; } //char max(char x, char y){ // return x > y ? x : y; // } int main( ){ int num=1; char ch=2; int num = 200; // 超过char范围(-128~127) long l = 123456789L; double d = 987654321.0; max(num,num); //调用max(int,int) max(ch,ch); //调用max(T,T),若char max没被注释优先调用char max max(num,ch); //调用max(int,int),char→int // max(num, 'a'); //调用max(char,char ),int→char。 //前提是没用int max(),否则会优先char转化成int。 max(l, d);//调用max(T,T),long→double return 0; }

max模板和max类都满足条件,编译器该怎么判定?

那就来看看匹配规则吧。

在这里优先级从高到低分为四类:

1、完全匹配。

当普通函数和模板都完全匹配时,编译器优先选非模板的普通函数。

如:max(num,num)满足两个函数,那就优先调用普通函数int max()。

2、提升转换(char和short转换为int,及float转换为double)

完全匹配的优先级远高于需要转换,优先调用和实例化完全匹配的函数。

如:max(ch,ch),模板完全匹配可以直接用(完全匹配),而普通函数还要将ch转换为int类型(提升转换),那就优先调用完全匹配的函数模板。

max(num,ch),函数模板只能传入一个类型,这里有两个类型,不满足模板。只能将int隐式转换成char(优先满足提升转换的转换,没有才进行标准转换。)传入int max()。

3、标准转换(int转换为char,及long转换为double)

提升转换与标准转换同时出现,提升转换的优先级大于标准匹配,编译器优先调用满足提升匹配的函数。

如:被注释调的代码 max(num, 'a');,在这里我们将int max()注释掉并且解开char max的注释,函数模板只能传入一个类型,这里有两个类型,不满足模板。只能将int转化成char,完成标准转换。

但注意的是,max(l, d)可以完成long→double的转换从而使用模板函数。

写到这时我发现个这里有一个小问题,前面模板类型使用不是说了max可以隐式转化,这里max(num,ch)中的ch不应该也被转化成int形了吗,那不应该可以传入模板?但实际 max(num,ch)根本传不进去,那为啥 max(l, d)的long→double的转换可以传入模板?

以下为我总结个人理解:

这是因为int与char之间的类型转换有争议,他既可以提升转换也可以标准转换,于是编译器根本不做任何隐式转换,类型推导T不一致,直接失败。

而long与double之间,推导失败后编译器通过模板实参显式指定(隐式的)完成了匹配long→double的隐式转换,并且不存在double→long逆方向,认为逆方向是不合理的,除非我们手动显示转换,不然类型推导T只有long→double着一种情况。

同理,float→double也是一样,他们只有标准转换和提升转换一种种情况。即使我们写了float max和long max,编译器依然不会逆向转换,主动调用模板函数。

PS:这一段理解的我头要秃了,这只是个人理解,还请酌情参考,和指出我的错误>_<

4、用户定义的转换,如类声明中定义的转换。

用户自定义的比较复杂,这里我在重写一个代码:

template <class T> T max(T x, T y){ return x > y ? x : y; } int max(int x, int y){ return x > y ? x : y; } class MyClass { public: int val; MyClass(int v=0) : val(v) {} operator int() const { return val; } bool operator>(const MyClass& o) const { return val > o.val; } }; int main( ){ MyClass obj1(5), obj2(3); // 自定义类型对象 max(obj1, obj2);// 调用模板函数max(MyClass,MyClass) max(obj1, 10);// 调用普通int函数(obj1→int + 10(int),模板推导失败) return 0; }

max(obj1, obj2)可以直接匹配模板(完全匹配),匹配int max需要转换类型。

max(obj1, 10);种,obj1和10的类型不同,匹配模板失败,只能转换obj1类型为int后匹配普通函数。

虽然obj1可以通过operator int() const { return val; } 重写成int型,但可惜模板推导阶段,编译器根本不会主动用operator int() 做转换,只 “机械” 推导实参的原始类型。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/10 8:11:07

还在卷 AI 编码?别傻了,拖垮你效率的根本不是写代码

提升研发效率的关键&#xff0c;也许不是AI&#xff0c;而是开发者体验最近&#xff0c;整个技术圈都在为 AI 疯狂。我们讨论 AIGC&#xff0c;讨论 AI Agent 如何重塑工作流&#xff0c;但这可能只说对了一半。我们用 AI 加速了“写代码”&#xff0c;但我发现&#xff0c;自己…

作者头像 李华
网站建设 2026/4/13 23:58:45

[APM32F1] APM32F103 CAN 丢包元凶:省成本用 HSI

一、概述近期有客户在基于 APM32F103 的电动两轮车项目研发中&#xff0c;遭遇了CAN 通信频繁丢包的棘手问题&#xff1a;调试时发现&#xff0c;F103 每发送7-8 帧数据&#xff0c;往往仅能收到 1 帧来自电机控制器的回复。经笔者现场排查分析&#xff0c;原因挺典型&#xff…

作者头像 李华
网站建设 2026/4/10 3:31:21

Python语法基础笔记(五)

一、函数含义&#xff1a;将独立的代码块组织成一个整体&#xff0c;使其具有特殊功能的代码集&#xff0c;在需要的时候再去调用即可作用&#xff1a;提高代码的重用性&#xff0c;执行体代码看上去更加简练基本格式定义函数&#xff1a;def 函数名&#xff08;&#xff09;&a…

作者头像 李华
网站建设 2026/4/14 7:14:26

系统中断损失百万:软件版本升级管控的实战避坑指南

系统中断损失百万&#xff1a;软件版本升级管控的实战避坑指南引言&#xff1a;别让一次小升级造成大损失 在2025年的企业IT管理中&#xff0c;软件版本升级早已不再是简单地“点个更新按钮”就能解决的事情。很多企业都曾遭遇过因版本升级不当导致的系统中断&#xff0c;损失高…

作者头像 李华
网站建设 2026/4/13 15:39:49

[SDOI2016] 征途题解

P4072 [SDOI2016] 征途 题目描述 Pine 开始了从 SSS 地到 TTT 地的征途。 从 SSS 地到 TTT 地的路可以划分成 nnn 段&#xff0c;相邻两段路的分界点设有休息站。 Pine 计划用 mmm 天到达 TTT 地。除第 mmm 天外&#xff0c;每一天晚上 Pine 都必须在休息站过夜。所以&…

作者头像 李华