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

第十三站:深度学习模板

一、基础:类型模板参数

定义

用 class / typename 声明,代表一个数据类型,在实例化时传入类型名,参数占位符 T 代表任意类型(int、自定义类、指针等),实例化尖括号内必须填类型

举例:
#include<iostream>using namespace std;// 类型模板参数,class / typename 完全等价template<classT>// template<typename T> 等效写法classA{};intmain(){return0;}
小结:

1.class 和 typename 在模板参数列表中无区别,均可用来声明类型参数

二、传统固定数组模板的缺陷(#define 全局常量)

举例:
1.固定长度为100
#defineN100template<classT>classArray{public:private:T_a[N];// 数组长度由全局宏N固定};intmain(){Array<int>a1;return0;}
2.固定长度为1000
#defineN1000template<classT>classArray{public:private:T_a[N];};intmain(){Array<int>a1;return0;}

这种定义方法带来的最核心的问题就是宏N是全局唯一常量,整个程序中只能有一种长度,导致无法在同一个程序中同时创建同类型,不同长度的数组对象。

三、非类型模板参数(解决宏的缺陷)

定义

不用 typename/class,用一个参数代表一个常量值,实例化时传值

template<intN,doubleX>// N、X 是非类型模板参数structArray{};
举例:

类型参数 + 整型非类型参数

// T:类型参数;N:int型非类型模板参数template<classT,intN>classArray{public:private:T_a[N];};intmain(){Array<int,100>a1;// int数组,长度100Array<int,1000>a2;// int数组,长度1000return0;}
注意:

1.非类型模板参数只能是整型常量

  1. 浮点数(double,float)、类对象以及字符串是不允许作为非类型模板参数的。

  2. 非类型的模板参数必须在编译期就能确认结果

非类型模板参数允许的类型:编译时报错如下

非类型模板参数允许的类型:编译时情况如下,没有报错
仅支持编译期可求值的常量整数类型: 整型:char/short/int/long/longlong/无符号整型

四,模板的特化

模板的特化定义

函数模板/类模板是一套通用的代码,但是有些特殊类型不能用这套通用的代码,我们就需要针对这种特殊类型对模板做一个特化就叫做模板的特化。

模板的特化又分为函数模板特化和类模板的特化

函数模板特化(全特化:即是将模板参数列表中所有的参数都确定化。)

特化步骤:

  1. 必须要先有一个基础的函数模板

  2. 关键字template后面接一对空的尖括号<>

  3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型

  4. 函数形参表: 必须要和模板函数的基础参数类型完全相同

//函数模板特化template<classT>boolIsEqual(T&left,T&right){returnleft==right;//直接用 == 比较两个同类型变量//缺陷:char* 字符串指针比较只会对比地址,不会对比字符串内容}//针对char*类型的特殊化处理// template<> 代表无剩余模板参数,全特化template<>boolIsEqual<char*>(char*&left,char*&right){// strcmp 逐字符对比字符串内容returnstrcmp(left,right)==0;}intmain(){inta=0,b=1;cout<<IsEqual(a,b)<<endl;// 调用通用模板,输出0constchar*p1="hello";constchar*p2="world";cout<<IsEqual(p1,p2)<<endl;// 调用char*特化版本,输出0return0;}
小结:

全特化语法:template<> 空模板头,显式指定类型 <char*>

类模板特化(包含全特化和偏特化)
全特化

全特化即是将模板参数列表中所有的参数都确定化。

举例:

//类模板全特化//1. 通用双参数类模板template<classT1,classT2>classData{public:Data(){cout<<"原模版类:Data<T1, T2>"<<endl;}private:T1_d1;T2_d2;};//2. 针对 Data<int, char> 的全特化template<>// 空模板头 = 全特化classData<int,char>//类型全部写死{public:Data(){cout<<"全特化: Data<int, char>"<<endl;}private:// 可完全重写成员,和通用模板结构无关};//测试intmain(){Data<int,int>d1;// 通用模板,输出 :原模版类:Data<T1, T2>Data<int,char>d2;// 匹配全特化,输出 :全特化: Data<int, char>return0;}
偏特化(包含参数固定型偏特化和类型修饰偏特化)

参数固定型偏特化:只固定一部分模板参数,剩余参数仍保留泛型,称为偏特化。【偏特化只存在于类模板,函数模板不支持偏特化。】

//类模板偏特化//1. 通用双参数类模板template<classT1,classT2>classData{public:Data(){cout<<"原模板类:Data<T1, T2>"<<endl;}private:T1_d1;T2_d2;};//2. 针对 Data<int, char> 的全特化template<>// 空模板头 = 全特化classData<int,char>//类型全部写死{public:Data(){cout<<"全特化: Data<int, char>"<<endl;}private:// 可完全重写成员,和通用模板结构无关};// 偏特化:T1固定int,T2仍为可变类型template<classT2>//部分参数写死,剩余泛型classData<int,T2>{public:Data(){cout<<"偏特化: Data<int, T2>"<<endl;}private:};//测试intmain(){Data<int,double>d1;//偏特化: Data<int, T2>Data<int,float>d2;//偏特化: Data<int, T2>Data<int,char>d3;//全特化: Data<int, char>Data<string,int>d4;//原模板类:Data<T1, T2>return0;}

小结:匹配优先级(从高到低):全特化 > 偏特化 > 通用基础模板

类型修饰型偏特化(不固定参数,只限制参数为指针 / 引用)

//类型修饰型偏特化// ////1. 通用双参数类模板template<classT1,classT2>classData{public:Data(){cout<<"原模板类:Data<T1, T2>"<<endl;}private:T1_d1;T2_d2;};//两个参数偏特化为指针类型template<classT1,classT2>classData<T1*,T2*>{public:Data(){cout<<"类型修饰型偏特化:Data<T1*, T2*>"<<endl;}private:T1_d1;T2_d2;};//两个参数偏特化为引用类型template<classT1,classT2>classData<T1&,T2&>{public:Data(constT1&d1,constT2&d2):_d1(d1),_d2(d2){cout<<"类型修饰型偏特化:Data<T1&, T2&>"<<endl;}private:constT1&_d1;constT2&_d2;};intmain(){Data<double,int>d1;//原模板类:Data<T1, T2>Data<int,double>d2;//原模板类:Data<T1, T2>Data<int*,int*>d3;//类型修饰型偏特化:Data<T1*, T2*>Data<int&,int&>d4(1,2);//类型修饰型偏特化:Data<T1&, T2&>return0;}

五, 模板分离编译

分离编译定义:

就是将声明和实现拆分到不同的文件中【一般在项目工程中会将函数或者类的声明放到.h文件中(对外暴露接口),将函数或者类的定义放到.cpp文件中(内部具体实现)】

优点

1.方便阅读和维护

2.对外只暴露接口,隐藏具体实现细节

注意(模板特殊限制)

普通函数 / 普通类可以正常分离编译,但类模板、函数模板不能简单分离编译: 模板实例化发生在编译期,若声明在.h、实现放在.cpp,调用时编译器看不到模板完整实现,会报链接错误。 解决模板分离编译的两种方案:1.模板实现直接写在.h 头文件中;2.在.cpp 末尾显式实例化需要的模板类型。

四步完整编译流程
C/C++ 编译分为 4 个独立阶段,每个.cpp单独编译为目标文件.o:

1.预处理:展开头文件、宏替换、删除注释,生成.i文件
2.编译:语法检查,翻译成汇编代码.s
3.汇编:汇编转机器码,生成二进制目标文件.o(每个 cpp 对应一个.o)
4.链接:合并所有.o,匹配函数地址,生成可执行程序

举例:普通函数分离编译和模板分离编译举例
//在Fun.h文件中声明#pragma oncevoidF1();// 仅函数声明,无函数体template<classT>voidF2(constT&x);//在Fun.cpp文件中实现#define _CRT_SECURE_NO_WARNINGS1#include"Fun.h"#include<iostream>using namespace std;voidF1(){cout<<"F1()"<<endl;}template<classT>voidF2(constT&x){cout<<"void F2(const T& x)"<<endl;}//在Test.cpp文件中测试#define _CRT_SECURE_NO_WARNINGS1#include"Fun.h"intmain(){//F1();F2(10);//编译通过,但LNK2019无法解析的外部符号 "void __cdecl F2<int>(int const &)" (??$F2@H@@YAXAEBH@Z),函数 main 中引用了该符号return0;}
普通函数 F1 为什么没问题?

1.编译Fun.cpp时,文件内直接定义void F1(),编译器直接实例化出F1机器码,写入Fun.o的符号表,记录函数内存地址

2.编译Test.cpp时,只看到F1()声明,语法校验通过,机器码里预留call F1(?)占位

3.链接阶段:读取Fun.o符号表,找到F1真实地址,填充Test.o的占位符,匹配成功

模板函数 F2 为什么链接失败?

模板只有被调用时才会实例化,不存在通用机器码:

1.Fun.c中只写了模板定义,但本文件没有任何F2(xxx)调用,编译器不会生成F2、F2等任何实例的机器码,Func.o符号表里没有 F2 相关符号

2.Test.cpp里执行F2(10),触发F2实例化,但Test.cpp只包含Func.h(只有模板声明,看不到完整函数体),无法生成F2机器码,仅预留call F2(?)占位

3.链接阶段:遍历Func.o找不到F2符号,抛出 LNK2019无法解析的外部符号 “void __cdecl F2(int const &)” (??$F2@H@@YAXAEBH@Z),函数 main 中引用了该符号

解决模板分离编译报错的 2种方案

1. 模板实现全部写在.h头文件,编译时能看见完整模板代码,调用时直接实例化。【推荐】

2.显式实例化(模板实现仍放.cpp)【不推荐】缺点:极不灵活,每新增一种调用类型就要手动加一行实例化代码

如下:

Func.cpp模板末尾,手动写出项目中会用到的全部类型,强制编译器生成对应实例机器码,写入符号表: template<classT>voidF2(constT&x){cout<<"void F2(const T& x)"<<endl;}// 手动实例化需要的类型templatevoidF2<int>(constint&);templatevoidF2<double>(constdouble&);

小结:模板:在哪调用,在哪实例化,看不到完整函数体就无法生成机器码

总结:
模板的优点:

1.实现了代码的复用,节省资源;2.增强了代码的灵活性,便于代码维护

模板的缺点:

1.当模板实例化过多会导致代码膨胀问题,也会导致编译时间变长;2.如果模板编译出现错误时,不易定位

http://www.jsqmd.com/news/1229994/

相关文章:

  • 西安经开商圈溯源收表门店盘点,公安备案签订正规交易单据 - 讯息早知道
  • 深入解析AM43xx SoC调试架构:从JTAG、CoreSight到多核协同调试实战
  • 惠州人黄金变现必看,五家正规上门回收店全流程实测与诚意指南 - 人间烟火小记
  • 输出可视化图像的方法
  • TaskoMask中的DDD实践:领域驱动设计在任务管理系统中的终极应用案例
  • 在大连卖黄金怎么才不被坑?亲历五家门店横向对比,附避雷指南 - 人间烟火小记
  • 计算机小程序毕设实战-大学生实训实践教学管理移动端系统设计 基于 Android 的高校实践教学任务管理 APP 高校实践教学过程跟踪管理系统的【完整源码+LW+部署说明+演示视频,全bao一条龙等】
  • Codex 接入踩坑记:当 AI 开始改生产代码,权限与日志成了最大拦路虎
  • 2026 年当下,寒亭知名的污水处理絮凝 AB 剂供应厂家哪家好,别再花冤枉钱!絮凝剂的秘密成本揭秘 - 企业推荐官【认证官方】
  • geo优化系统哪家好?靠谱服务商推荐|北京泛海明心 - 米諾
  • LoRa 协议完整解析 + 代码实战案例
  • 【SI_高速串行信号03】快速掌握高速串行信号示波器均衡技术操作指南
  • 卡地亚官方公告|太原2026年7月最新网点地址与客服热线一览,售后无忧 - 卡地亚官方售后中心
  • 【高速缓存】RedisVL 指南:从向量搜索到 AI 应用落地
  • 2026 年更新:汕尾专业的园林石牌坊供应厂家哪家专业,打破传统:石牌坊如何重塑现代园林风貌? - 企业信息推荐【官方】
  • 荆州武校排名前十,黄龙文武学校为什么是荆州家长首选? - 圣龙武术朱老师
  • 2026宿迁黄金回收避坑指南:4家正规门店实测对比公布 - 生活测评君
  • 2026世界人工智能大会 | 启鸣达人首发《世界模型驱动的教育AGI白皮书》
  • 宁波黄金处理前先掌握纯度称重票据品牌这四项准备能省不少心 - 大牌深度测评
  • 日志审计别再人肉搬砖:RPA+AI 让 GXP 合规从“天“缩到“分钟“
  • 宝可梦Roguelike离线畅玩终极方案:Pokerogue-App完整指南
  • AI音频降噪的“黑箱”终于被打开:可视化STFT-GAN特征图层+注意力热力图,精准定位残留噪声源(附Jupyter可交互分析模板)
  • 2026 青岛本地生活代运营实力测评榜 同城获客 + 短视频全维度优选 - GrowthUME
  • Flutter 鸿蒙Text组件详解
  • 2026 青岛钻石回收套路拆解,正规门店挑选干货,钻戒变现不踩雷 - 奢侈品回收机构参考
  • 深度解析OpenCore Legacy Patcher:如何让老旧Mac焕发新生运行最新macOS
  • 2026海口名表变现怎么找靠谱商家?线下实体门店甄选细则推荐 - 一日一测评
  • 2026 常州本地奢侈品洗护服务 TOP8 门店实测盘点|皮具清洗修复、名表保养哪家专业 - GrowthUME
  • 能大量供货的糕点生产厂家选购指南 - 全域品牌推荐
  • 茂名黄金回收哪家强?茂南电白高州化州信宜五家正规门店实测推荐 - 人间烟火小记