"抱紧你的我,比国王富有"
C++可复用性高,C++引入了模板的概念,后面在此基础上,实现了方便开发的标准模板库STL
-----前言
一、初始模板
我们先来看看 下面的代码段;
如果此时又有需求: 交换一个char 类型的变量 ? 我们是不是又得费点时间 码字?
肯定不行! 未免太 麻烦了。
(1)什么是模板?
这是一个 各样的模子。 如果我们想把这个图形染成绿色 直接可以在模板上进行染色。
如果是想要鲜艳的红色, 直接可以在模板的基础上 浇筑红色颜料即可。
还需要去 重新规划 形式 + 颜色吗? 答案是 不需要!
C++的模板分为两类: 函数模板 + 类模板。
我们现在就针对 这两种模板 入手。
(2)模板的使用
①函数模板
template<class T>
template<typename T>
//函数体
void(T / T&) Func(T a1);
//注意:typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class)
以此,我们也就 不用再多写一个" swap" 对char 类型单独进行特写。
②类模板
那么模板函数类型 与 普通函数有什么区别呢? 两个是否可以同时存在? 编译器会怎样取舍?
那么此时我们如果非要使用模板的 情况,就需要 显示调用模板。 也就是第二次调用。
(3)模板的原理
了解的 函数模板的基本使用, 对于模板的原理 也有了一定的基础。
函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。所以其实模板就是将本来应该我们做的重复的事情交给了编译器。
只有当模板进行实例化 (调用时)后 才会,生成具体的函数!
由此,模板是不支持分离、编译的!
①模板类型 与 隐式类型转换
因为在"编译期间",当编译器看到该实例化时,需要推演T 接受的传参类型。
但模板参数列表中只有一个T,编译器无法确定此处到底该将T确定为int 或者 double类型而报错。
那如果不更变代码,如何进行使用呢?
1.显示类型转换
2.用户自定义强转(隐式类型转换)
3.多类型参数(但与提问冲突)
二、模板进阶
(1)非类型模板参数
模板参数分类类型形参与非类型形参
类型形参:如前面的 class typename。非类型形参:实质是一个被当做常量来使用。
如:C++中也有一个array的容器,也是用了这个非类型的模板参数,实现的是一个静态的数组。
注意:
浮点数、类对象以及字符串是不允许作为非类型模板参数的。
非类型的模板参数必须在编译期就能确认结果。
三、模板特化
通常情况下,使用模板可以减少一些类型无关代码的编写,但是对于一些特殊类或类型,可能需要特殊的处理,才能达到我们想要的效果。
(1)函数模板化
1.必须要先有一个基础的函数模板.
2.关键字template后面接一对空的尖括号<>.
3.函数名后跟一对尖括号,尖括号中指定需要特化的类型.
4.函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误.
template<>
void Func<typname>(typename f1,typname f2)
{}
如我们所见,对于日期类,整型int可以得到正确的结果,
但是对比指针p1,p2时,结果却并不如我们所预。 其原因就在于,p1,p2分别是d1,d2的地址,他们比较的是地址的大小。
为此,我们实现的less模板仿函数,显然无法满足我们的需求。
除开用模板的特化解决外,我们仍然可以就使用一个普通函数,专门针对日期类地址的类型。当然这是利用编译器的机制,会优先调用不用进行推演的合适的函数。
bool Less(Date* left, Date* right)
{
return *left < *right;
}
这下就可以用到我们的模板特化,问题由此得到解决!
(2)类模板特化
①全特化
全特化即是将模板参数列表中所有的参数都确定化;
②偏特化
任何针对模版参数进一步进行条件限制设计的特化版本;
部分特化;
类型的进一步限制;
总结:
①模板分为函数模板和类模板,模板参数分为类型参数和非类型模板参数(常量)
②模板不支持分离编译的根本原因在于:
程序在连接时,会去找模板的地址进行链接,但是模板只会在实例化时,才生成具体的代码,而在编译阶段不会处理,也就生不成具体的代码。因此,链接就会报错。
③模板特化有函数模板特化和类模板特化。类模板特化值得注意的是:全特化与偏特化。偏特化即可以确定类型,也会限制类型。
本篇到此结束,感谢你的阅读。
祝你,向阳而生~