1、通过一个简单的例子来理解模板的用途:

模板为不同类型的数据生成操作相同或相似的函数。
弱语言如Python,可以使用一种函数来应对各种类型,但是C++就不得不为不同的类型编写相似的函数。模板的作用就是把这一步骤交给编译器去执行,让这些函数在编译器生成。
 
2、模板参数的自动推导
原则:凡是可以推导出来的模板参数“值”就无需在模板实参列表中写明。
规则一:编译器值根据函数调用时给出的实参列表来推导模板参数值,与函数参数类型无关的模板参数无法推导
规则二:与函数返回值相关的模板参数其值也无法推导
规则三:所有可以推导模板参数必须是连续位于模板参数列表尾部,中间不能有不可退到的模板参数。
举例:
 

test1~test3的分析过程如下:
第一,sv2是返回值,我们不能通过返回值的类型推导模板参数的值,所以T2无法推导出来;
第二,T0使用的地方是函数内部的一个变量,也不是函数的参数,所以无法通过sv0的类型推导出T0的值
第三,以test1为例,func的三个参数分别是1,2,3整型,所以可以推断出T1、T3、T4是int。因为T0和T2无法推导出,所以必须在func<>中明确给出。而由于要根据声明的顺序给出,不能跳过T1,所以func<>中的三个类型分别是T0、T1、T2的类型。
 
3、模板参数的默认值
形如:
有两个具有默认类型,其它三个可以从函数参数类型推导,所以不需要尖括号:
 
4、模板参数的静态变量
这一讲用来说明,如果模板参数的值是相同的,那么模板函数实例就只生成一个,不会重复生成。
 

test1和test2中的static变量进行了递减,说明test1和test2中的func是同一个。这也就说明,func的实例是依具体的参数而决定的,如果模板参数值一样,编译器就不会重复生成函数实例。
我们逆向也可以知道,这里的两个函数是相同的:

用IDA加载进符号表之后,会更清晰一点:

5、问题一:模板函数应该如何在头文件里声明?
 
如果只是在头文件里添加一个函数模板的声明:

这样编译工程是肯定能编译通过的。因为,编译器在编译test.h和test.cpp文件时,只是读到了func0函数模板的实现,并没有读到任何需要生成函数模板的实例的语句,所以不会生成任何func0函数实例。

但是如果在main.cpp中添加了func0,那么此时就要生成一个func0的具体实例了,但test.h文件中只有一个func0函数模板的声明,编译器并没有生成实际的函数实例,只好在mian函数中的func0处预留一个链接调用,等待在链接过程中找到函数实现。因为你这里调用时,实际上是调用一个func0<int>(0); 但是test.cpp里并没有这个函数的实力。就会报错(这种错误很隐晦,很难排查):

所以,我们的解决办法是明确地在头文件中声明具体的实例。编译器就会生成这个函数模板实例了:

问题二:
但是,如果你想再生成func0(float)、func0(char)那么就得在头文件中添加两个实例的声明。但这貌似违背了最初使用模板的目的。
解决的办法就是把模板的实现也包含到头文件中,这样main函数把test.h包含进来了,编译器在编译时就知道main函数要用到一个func0<int>实例了,就可以编译出func0<int>实例了。//如果你把代码放到cpp里,那就是等到连接时去做事儿,如果放到h文件里,那就是在编译时去做事儿。
 
问题三:
产生重复模板实例问题。但这个问题会由连接器解决。
比如,新增加caller.obj

原有的main.obj中也有

但我们会发现main和caller.obj最终调用的都是caller中的func0:

就是因为连接器把caller0和main中的func0合并成了一个,合并规则是函数名、模板实参列表以及参数列表相同。
 
 
 
 

最新文章

  1. Discuz NT 架构剖析之Config机制
  2. C#:如何解决WebBrowser.DocumentCompleted事件的多次调用
  3. FLUSH TABLES WITH READ LOCK
  4. mongoose学习笔记1--基础知识2
  5. 如何快速使用ECharts绘制可视化图表
  6. android利用反射通过代码收缩通知栏
  7. 【二分】【高精度】Vijos P1472 教主的集合序列
  8. Linux BFS简介
  9. POJ1469_COURSES(二部图最大匹配)
  10. vb.net它SqlHelper制备及应用
  11. Android - 采用ApiDemos得知Android开展
  12. 五笔拼音反查精灵 v6.69 绿色版
  13. Jsp运行环境——Tomcat
  14. [Swift]LeetCode691. 贴纸拼词 | Stickers to Spell Word
  15. MySQL 执行计划中Extra(Using where,Using index,Using index condition,Using index,Using where)的浅析
  16. 【Java】 剑指offer(43) 从1到n整数中1出现的次数
  17. spring源码分析系列 (8) FactoryBean工厂类机制
  18. [sh]getopt参数解析
  19. Write your own Terraform provider: Part 1
  20. Hibernate学习笔记2.3(Hibernate基础配置)

热门文章

  1. 安装wget 、 wget命令
  2. centOS7搭建hadoop,zookeeper,hbase
  3. js怎么动态加载js文件(JavaScript性能优化篇)
  4. mybatis之动态SQL操作之更新
  5. JS选择器querySelector和~All,三个原生选择器
  6. JVM源码分析之JDK8下的僵尸(无法回收)类加载器[z]
  7. 2.使用kubeadm快速搭建k8s集群
  8. Laplacian eigenmap 拉普拉斯特征映射
  9. 【Linux】配置SSH免密登录
  10. 从a-zA-Z0-9特殊字符中生成指定数量的随机字符密码的多层for循环跳出