个人主页:仍有未知等待探索-CSDN博客
专题分栏:C++
欢迎指教!
目录
一、非类型模板参数
模板参数有两类:类型形参和非类型形参。
类型形参:出现在模板列表中,跟在class和typename之类的参数类型名称。
非类型形参:用一个常量作为类(函数)模板的参数,在模板中可以当作常量使用。
注意:
1. 浮点数、类对象以及字符串是不允许作为非类型模板参数的。
2. 非类型的模板参数必须在编译期就能确认结果。
这里的T就是类型形参,而N就是非类型形参。
二、模板的特化
1、概念
在某些特殊的情况下,某些问题不能用通用的方法来进行解决,需要用到一些特殊的解决方式。
// 加入T要是指针类型的话,就会有错误,本意是用指针指向的值来进行比较 // 但是,实际上是用指针(地址)来进行比较 // 函数模板 template <class T> bool compare(const T& x, const T& y) { return x < y; }
所以,会需要用到模板的特化,来对这种特殊情况进行特殊处理。
2、函数模板特化
就接着以日期比较大小函数为例,解决模板参数是指针类型的情况,对其进行特化。
// 简单实现一个日期类 class Date { public: Date() :_year(0) {} //。。。小于 操作符重载 private: int _year; }; // 对日期类进行比较大小 template<class T> bool compare(const T& x, const T& y) { return x < y; } // 对日期类指针进行的特化 // 这个‘<’ 操作符要进行操作符重载,写在日期类里面,我这里省略了 template<Date*> bool compare(Date* x, Date* y) { return *x < *y; }
3、类模板特化
1、全特化
全特化即是将模板参数列表中所有的参数都确定化。
// 数据类
template<class T1, class T2>
class Data
{
public:
Data() {cout<<"Data<T1, T2>" <<endl;}
private:
T1 _d1;
T2 _d2;
};
// 对于T1 = int, T2 = char这种类型的特化
template<>
class Data<int, char>
{
public:
Data() {cout<<"Data<int, char>" <<endl;}
private:
int _d1;
char _d2;
};
void TestVector()
{
Data<int, int> d1;
Data<int, char> d2;
}
2、偏特化 (半特化)
任何针对模版参数进一步进行条件限制设计的特化版本。
不要片面的理解偏特化!!!
对于下面的类,其偏特化的形式:
template<class T1, class T2> class Data { public: Data() {cout<<"Data<T1, T2>" <<endl;} private: T1 _d1; T2 _d2; };
a.部分特化
这也是大部分人都明白的一种偏特化的形式。
// 将第二个参数特化为int template <class T1> class Data<T1, int> { public: Data() {cout<<"Data<T1, int>" <<endl;} private: T1 _d1; int _d2; };
b. 参数的更进一步的限制
偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本。
可以将两个参数限制为是指针类型或者是引用类型。
//两个参数偏特化为指针类型 template <typename T1, typename T2> class Data <T1*, T2*> { public: Data() {cout<<"Data<T1*, T2*>" <<endl;} private: T1 _d1; T2 _d2; }; //两个参数偏特化为引用类型 template <typename T1, typename T2> class Data <T1&, T2&> { public: Data(const T1& d1, const T2& d2) : _d1(d1) , _d2(d2) { cout<<"Data<T1&, T2&>" <<endl; } private: const T1 & _d1; const T2 & _d2; };
三、模板的分离编译
1、什么是分离编译
一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。
2、出现的编译报错
// a.h template<class T> T Add(const T& left, const T& right); // a.cpp template<class T> T Add(const T& left, const T& right) { return left + right; } // main.cpp int main() { Add(1, 2); Add(1.0, 2.0); return 0; }
原因:
用一句简单的话来总结就是:有的“人”不用,用的“人”没有。
在a.cpp里面有Add的地址,而在main.cpp里面没有Add的地址。在链接的时候,main.cpp找不到Add的地址,所以就会报错。
模板成员函数不支持声明定义分离在两个文件,原因是:声明的地方知道实例化的T是什么类型,但是只有函数声明而没有定义。定义的地方不知道实例化成什么类型,但是有函数的定义。(链接前,两个文件不会互通,会影响效率)
解决方案:
1. 将声明和定义放到一个文件 "xxx.hpp" 里面或者xxx.h其实也是可以的。推荐使用这种。
2. 模板定义的位置显式实例化。这种方法不实用,不推荐使用。template class array<int> template class array<double>
四、总结
优点
1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生。
2. 增强了代码的灵活性。
缺陷
1. 模板会导致代码膨胀问题,也会导致编译时间变长。
2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误。
五、其他
按需实例化:实例化模板不是全部实例化,而是用哪一个就实例化哪一个。
这个按理来说应该是要报错的,但是事实上确实没有报错。
模板:根据模板实例化-》半成品模板-》实例化成具体的类/函数-》语法编译。
模板支持缺省参数、非类型模板参数(整型)
函数的传参:在运行时传参。
模板的传参:在编译时传参。
有特化走特化,没特化走原模板
特化:单独写不行,必须写在模板的下面。针对某些特殊类型,可以特殊处理
(函数模板不建议特化 ,建议重载)
编译时如果只有声明,那就链接的时候根据地址去找定义。
编译时如果声明和定义都有,直接进行实例化。