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

深入理解C++模板

一.函数模板

1.模板的意义

假如我们要写一系列函数,用于比较两个数的大小,但是两个数的类型有很多种,可以是整形,浮点型,可以是字符类型,那么常规的方法,是为每一个类型都写一个函数,但是我们会发现,这很麻烦,并且进行比较的函数都十分的相似,只有参数列表不同。为了在这样的情况中简化程序,我们可以使用函数的模板,所谓函数的模板,实际上就是对函数的形参类型进行了参数化。

2.函数模板的实现

如图:我们实现支持多种类型的比较函数:

这里的typename也可以用class替换。

当我们要传入两个整形进行比较时可以通过compare<int>(10,20)(这里的compare实际上上一个模板,模板名加参数列表后才称为函数);来调用。

我们称这里的compare<int>(10,20)为函数的调用点,在函数的调用点,编译器用用户指定的类型,从原模板实例化一份函数代码出来(也就是说,在编译器的角度,仍然是编译多个模板函数(为compare的重载函数),编译的函数数量并未减少)。

但同时这里也可以直接使用compare(10,20)来调用,这是由于函数模板存在模板实参推演,可以通过用户传入的参数推导出来模板的类型。

但是如果我们这里compare(10,20.5)那么这里就无法正确推演出T的具体类型为什么,就会出现错误,但是这时如果我们使用compare<int>(10,20.5),这里就不会出现错误,会将double类型的数据强转为int类型。

由于函数模板不确定T的类型,所以函数模板不进行编译,编译的是模板函数。

我们再来看,假如我们要比较两个字符串,使用compare("aaa","bbb"),从结果上看确实能够进行比较,但是模板识别的T为const char* 这里比较使用>实际上是两个字符串的地址大小,明显与我们的期望不符,比较字符串我们应该使用strcmp等字符串比较函数,不能直接>。

面对这种情况,我们就要对字符串单独采用一个模板实现,即模板的特例化(专用化)如图:

如果我们再定义一个非模板函数(普通函数)也进行字符串的比较,那么编译器会优先调用非模板函数(更为高效)。

由于函数模板不进行编译,所以在多文件工程中,不能在一个文件中声明使用模板,在另一个文件中实现模板,但对于特例化的模板可以正常使用(因为模板的特例化,实际上生成了一个模板函数)所以一般模板都是定义在头文件中,在源文件中直接#include(##include导入的头文件直接在预编译阶段展开)。

当然,这种情况也是有办法解决的(不过并不推荐),我们可以在函数模板定义后,直接告诉编译器进行指定类型的模板实例化(如:templete bool compare<int>(int,int))。

模板非类型参数:我们可以在模板中定义非类型参数,不过只能使用整数类型(地址,引用也可),并且为常量。

二.类模板

如下:我们利用模板实现一个顺序栈:

template<typename T> class stack { private: vector<T>mpstack; public: stack() { } public: //入栈操作 void push(T val) { mpstack.push_back(val); } //出栈操作 void pop() { if (mpstack.empty()) { throw "mpstack is empty"; } mpstack.pop_back(); } //获取栈顶操作 int top()const { if (mpstack.empty()) { throw "mpstack is empty"; } return mpstack.back(); } //判断栈是否为空 bool empty()const { return mpstack.empty(); } };

此时stack已经不在是一个类,而是一个类模板,此时的类名称为模板名称+类型参数列表。

同时,对于所有所有到类的地方,除了构造与析构函数不用加<>,其他地方最好都加上(虽然不加编译器会自动帮我们加)。

类模板会进行选择实例化,如果我们利用模板类定义了一个对象,编译器并不会将类中所有的成员方法都进行实例化,而是看我们调用了那些。

http://www.cnnetsun.cn/news/2071234.html

相关文章:

  • 告别WSL2的snap安装烦恼:一键配置systemd并管理你的Ubuntu服务
  • 考研复习 Day 19 | 数据结构与算法--图(下)
  • 情感分析技术:原理、实现与应用全解析
  • Model Context Protocol:机器学习模型全生命周期管理的关键
  • 别再乱用if-else了!Verilog条件语句的5个实战避坑指南(附代码对比)
  • 快手万人组织的 AI 研发范式跃迁和落地实践
  • 3分钟搞定!Windows系统显示iPhone HEIC照片缩略图终极指南
  • Betaflight飞控系统架构解析与技术实现方案
  • 如何实现微信聊天记录永久保存:WeChatMsg本地化数据管理革命
  • 2026年必知!那些便携又好带,让人欲罢不能的青岛特产!
  • Windows逆向实战:手把手教你用WinDbg和OD定位TEB结构(含FS寄存器详解)
  • 别再乱开V-Sync了!游戏开发者必懂的垂直同步原理与实战避坑指南(附OpenGL/DirectX代码)
  • Elsevier Tracker:学术投稿效率神器终极指南
  • 别再手动敲测试数据了!Vivado仿真中$readmemb/h的6种用法详解与避坑指南
  • ANSYS APDL新手避坑指南:用悬臂梁案例搞定你的第一个静力学分析
  • 如何用PodcastBulkDownloader一键备份你喜爱的播客内容?
  • 论文“瘦身”新革命:书匠策AI,让学术文字焕发新生!
  • MarkDown时序图进阶:巧用并行、条件与循环构建复杂交互逻辑
  • 手把手教你用DSP28335的定时器中断实现增量式PID控制(附完整代码)
  • 【C++26反射元编程实战图谱】:含完整UML架构设计图+AST遍历时序图+编译期契约检查模板(附GitHub私有仓库邀请码)
  • 别再手动存localStorage了!用Vue的keep-alive搞定Ruoyi后台页面状态保留(附完整配置流程)
  • 从零搭建ROS2机器人仿真环境:Gazebo+Rviz2联合调试完整指南(含传感器配置)
  • 从‘等比例缩小’到‘等效缩减’:一文看懂芯片制程演进背后的材料与结构‘魔法’
  • C23标准内存安全扩展深度解密(std::memsec.h草案+bounded_array_t+safe_ptr_t),2026年前必须掌握的5个迁移路径
  • 手搓FPGA版SoftMax:除了泰勒展开,硬件实现指数和倒数还有哪些‘骚操作’?
  • 从芯片功耗管理实战出发:手把手配置SDC中的电压岛、电平转换器与动态/静态功耗约束
  • 终极指南:如何在Windows上使用iperf3快速准确测试网络性能
  • 当AI开始互相偷密码:77万社交机器人集体裸奔,揭开Agent时代的致命漏洞
  • 别再死记硬背快排模板了!用洛谷P1177为例,图解快速排序与归并排序的核心思想与调试技巧
  • 怎样高效压缩视频图片:3步掌握CompressO跨平台压缩神器