尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
C++模板:泛型编程、函数模板与类模板详解
文章目录模版1. 泛型编程2. 函数模版2.1函数模版格式2.2 函数模版原理2.3 函数模版的实例化2.4 模版参数的匹配原则3. 类模版3.1 类模版的定义格式3.2类模版的实例化模版1. 泛型编程在C语言中要写交换函数时要针对不同的参数类型来写多个对应的函数void swap_int(int x,int y) { int tmpx; xy; ytmp; } void swap_double(double x,double y) { double tmpx; xy; ytmp; } //………这未免有点麻烦了在C中有我们可以使用函数重载但这也不过是将函数名统一还是比较麻烦有没有什么一劳永逸的办法呢这就要提到泛型编程了。泛型编程编写与类型无关的通用代码是代码复用的一种手段。模板是泛型编程的基础模版他像一个模具一样只要写一次完整的语法规则就能自己根据参数推导出匹配的函数模版分为两部分函数模版和类模版2. 函数模版2.1函数模版格式templatetypename T//也可以把typename改为class void swap(T x,T y) { T tmpx; xy; ytmp; }定义模板的是​template​​typename/class是用来声明类型形参的2.2 函数模版原理写好一份模版这相当于一份蓝图。这段代码不生成任何机器指令它只是生成函数的模具在编译期编译器发现调用进行实例化swap(2,3); //编译器推导出 Tint swap(2.1,3.3); //编译器推导出 Tdouble编译器发现调用时用int​、double​来替换T生成两份独立的函数编译器在内部生成对应的真实代码void swap(int x,int y) { int tmpx; xy; ytmp; } void swap(double x,double y) { double tmpx; xy; ytmp; }用了几种类型生成几个版本这就是模板的零成本抽象生成的代码跟手写的一样高效没有运行时开销。代价是编译时间变长、二进制体积变大因为每种类型一份。‍2.3 函数模版的实例化用不同类型的参数使用函数模板时称为函数模板的实例化。模板参数实例化分为隐式实例化和显式实例化。隐式实例化让编译器根据实参推导模版参数的实际类型templateclass T T Add(const T left, const T right) { return left right; } int main() { int a1 10, a2 20; double d1 10.0, d2 20.0; Add(a1, a2); Add(d1, d2); /*看看下面这个例子 Add(a1, d1);一个是int类型一个是double类型 该语句不能通过编译因为在编译期间当编译器看到该实例化时需要推演其实参类型 通过实参a1将T推演为int通过实参d1将T推演为double类型但模板参数列表中只有 一个T 编译器无法确定此处到底该将T确定为int 或者 double类型而报错 注意在模板中编译器一般不会进行类型转换操作因为一旦转化出问题编译器就需要背黑锅 */ // 此时有两种处理方式1. 用户自己来强制转化 2. 使用显式实例化 // 强制类型转换 Add(a1,(int)d1); return 0; }显式实例化在函数名后的​中指定模版参数的实际类型int main(void) { int a 10; double b 20.0; // 显式实例化 Addint(a, b); return 0; }2.4 模版参数的匹配原则一个非模板函数可以和一个同名的函数模板同时存在而且该函数模板还可以被实例化为这个非模板函数// 非模版函数 专门处理int的加法函数 int Add(int left, int right) { return left right; } // 通用加法模版函数 templateclass T T Add(T left, T right) { return left right; } void Test() { Add(1, 2); // 与非模板函数匹配编译器不需要实例化 Addint(1, 2); // 调用编译器实例化的Add版本 }对于非模板函数和同名函数模板如果其他条件都相同在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数那么将选择模板// 专门处理int的加法函数 int Add(int left, int right) { return left right; } // 通用加法函数 templateclass T1, class T2 T1 Add(T1 left, T2 right) { return left right; } void Test() { Add(1, 2); // 与非函数模板类型完全匹配不需要函数模板实例化 Add(1, 2.0);// 模板函数可以生成更加匹配的版本编译器根据实参生成更加匹配的Add函数 //结果是3精度被截 //显式实例化Addint,double(1,2.0); }模版函数不能自动转换类型但是普通函数能够自动转换类型3. 类模版3.1 类模版的定义格式templateclass T1, class T2, ..., class Tn //注意每一个参数都要有关键字class与typename可以混用 class 类模板名 { // 类内成员定义 };#includeiostream using namespace std; // 类模版 templatetypename T class Stack { public: Stack(size_t capacity 4) { _array new T[capacity]; _capacity capacity; _size 0; } void Push(const T data); private: T* _array; size_t _capacity; size_t _size; }; // 模版不建议声明和定义分别写到到两个文件会出现链接错误 templateclass T void StackT::Push(const T data) { _array[_size] data; _size; } int main() { Stackint st1; // int Stackdouble st2; // double return 0; }3.2类模版的实例化类模板实例化与函数模板实例化不同类模板实例化需要在类模板名字后跟​然后将实例化的类型放在中即可类模板名字不是真正的类而实例化的结果才是真正的类。// Stack是类名Stackint才是类型 Stackint st1; // int Stackdouble st2; // double以vector为例#includevectorvectorint这里vector只是提供了一个数据结构具体的储存类型需要通过模版实例化才能推导出来‍同一个命名空间中定义了多个类每个类之前都写了一个模版参数​template class T在每个类中都使用T这样会有冲突吗没有​templateclass T里的 T 是模板参数它的作用域只限于紧跟其后的那个模板声明和它的定义类体/函数体之内。每个类模板各自开启一个独立的作用域彼此的 T 毫无关系在某些时候我们会看到这样使用typename的写法typedef typename rep_type::const_pointer pointer;前面我们提到了可以用class或typename来定义模版参数但这里使用typename明显不是定义模版参数在这里typename的作用是告诉编译器rep_type::const_pointer是一个类型名那为什么这里需要告诉编译器它是个类型名呢其实这句代码不是很完整我也不好把完整代码放过来太多了而且不是很直观。因为rep_type也是一个依赖模版参数的类在编译器编译到上面写出来的这句话之前还没有对rep_type::const_pointer进行实例化为什么不等到模板实例化时再判断因为编译器需要先解析模板的语法在模板参数具体确定之前它无法判断这个名字是类型还是变量。这就导致编译器在编译到上面这句话时会出现歧义模板代码里像rep_type::const_pointer这样的名字可能依赖模板参数。编译器在模板定义阶段还无法确定const_pointer究竟是类型还是其他成员因此用typename明确说明它是类型。typename不会创建新类型也不改变类型只是在模板代码中消除歧义。完
RELATED

相关推荐

40秒生成3分钟半的完整歌曲:Yue2-3B-GGUF长音频性能实测全报告(RTX 5090)

40秒生成3分钟半的完整歌曲:Yue2-3B-GGUF长音频性能实测全报告(RTX 5090)

40秒生成3分钟半的完整歌曲:Yue2-3B-GGUF长音频性能实测全报告(RTX 5090) 📌 一句话总结:Yue2-3B-GGUF 是 Yue2-3B 文生音乐模型的 audio.cpp 原生 GGUF 权重包。实测在 RTX 5090 上,Q8_0 量化组合约 39 秒…

📅 2026/10/30 2:24:49
GGUF vs BF16 vs vLLM:NVIDIA-Nemotron-3.5-Bolt-74B-A9B-GGUF 全平台部署路径选择指南(llama.cpp/vLLM/24GB版)

GGUF vs BF16 vs vLLM:NVIDIA-Nemotron-3.5-Bolt-74B-A9B-GGUF 全平台部署路径选择指南(llama.cpp/vLLM/24GB版)

GGUF vs BF16 vs vLLM:NVIDIA-Nemotron-3.5-Bolt-74B-A9B-GGUF 全平台部署路径选择指南(llama.cpp/vLLM/24GB版) 如果你正在寻找 NVIDIA-Nemotron-3.5-Bolt-74B-A9B 的本地部署方案,本文帮你一次讲清三条路线怎么选:用…

📅 2026/10/30 2:24:49
LTX-2.5 两阶段超分工作流:ltx-2.5-mlx-q8 中空间 2x 与时间 2x 上采样器原理详解

LTX-2.5 两阶段超分工作流:ltx-2.5-mlx-q8 中空间 2x 与时间 2x 上采样器原理详解

LTX-2.5 两阶段超分工作流:ltx-2.5-mlx-q8 中空间 2x 与时间 2x 上采样器原理详解 在 ltx-2.5-mlx-q8 仓库中,LTX-2.5 两阶段超分工作流由两个独立的潜空间上采样器组成:spatial_upscaler_x2(空间 2x 上采样)和 tempo…

📅 2026/10/30 2:24:49
MORE NEWS

更多资讯

📰

Axure原型设计入门:一张图掌握核心操作流程

很多刚接触原型设计的朋友,第一次打开Axure看到满屏的元件库、属性面板和交互面板,第一反应往往是“这玩意儿比写代码还复杂”。我见过太多人装了软件、看了两小时教程,然后默默关掉,转头继续用PPT画方块。问题不在于Axure难&…

📰

安全隔离网闸:物理隔离下的受控数据交换原理与实战配置

1. 安全隔离网闸到底在解决什么问题第一次接触“安全隔离网闸”这个概念,是在一个跨网络数据交换的项目里。当时业务方需要在两套物理隔离的网络之间同步一批文件,开发同学第一反应是“拉根网线不就行了”,运维同学则坚持“绝对不行&#xff…

📰

Transformer自注意力机制:置换不变性与位置编码解析

1. 理解Transformer自注意力的置换不变性当我们说Transformer的自注意力机制是"置换不变"的,本质上是在描述它对输入序列中元素的排列顺序不敏感的特性。想象你面前有一副扑克牌,无论你怎么洗牌(改变顺序),只…

📰

深入解析SQL COUNT函数:从基础语法到性能优化

1. 为什么我们需要深入了解COUNT函数?在日常数据库操作中,COUNT函数可能是最常用的聚合函数之一。但很多人只是停留在"SELECT COUNT(*) FROM table"这样的基础用法上,实际上这个简单的函数背后隐藏着许多值得深究的技术细节。我曾在…

📰

代码签名实战:密钥、时间戳与证书链验证指南

1. 代码签名到底在签什么:先把概念钉死我第一次接触“代码签名”这个词,是在给一个桌面工具做分发的时候。当时打包出来的安装包在别人电脑上双击就被拦下来了,弹窗提示“未知发布者”。我一开始以为是杀毒软件误报,换了几台机器测…

📰

5G毫米波与室内电波传播仿真技术解析

1. 项目背景与核心价值城市与室内电波传播仿真一直是无线通信系统设计中的关键难题。随着5G毫米波频段和物联网设备的普及,传统经验模型在复杂城市场景下的预测误差可达15dB以上。我们团队开发的这套高频电磁场仿真系统,专门针对280MHz-100GHz频段的传播…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬