一. 非类型参数模板
1.1 模板参数的类型:
(1)类型:在模板参数列表中,在class或者type那么之后的参数类型名称,通常将其看为一个变量
#define N 10
template<class T>
class stack
{
public:
void pop()
{
.....
}
private:
T _a[N];
size_t size;
}
(2)非类型:其中class T是类型模板参数,size_t N 是非类型模板参数,且实例化时传输的数据一定是个常量。非类型模板参数在 c++11 中只支持整形(char size_t int short)。
template<class T,size_t N>
class stack
{
public:
void pop()
{
.....
}
private:
T _a[N];
size_t size;
}
1.2 C++11 静态数组
C++ 11 添加了一个新的容器 array,array就用到了非类型模板参数,array本身是一个静态数组,我们在调用array容器时可以像以下方式这样写 .
那么这里这个array容器的优势就是可以很精准的检查出当前数组是否有越界的行为,a1[10]本质上是一个函数调用,array内部对[ ]进行了运算符重载, 而a2[10] 本身就是一个指针的解引用。但是vector本身也有一样的效果,array容器的存在意义并不是很大。
#include<array>
int main()
{
array<int,10> a1;
int a2[10];
}
二.模板的特化
定义:针对某些类型进行特殊化处理。
2.1 类模板的特化
对于不同的类型的数据进行不同的处理。
template<class T1 , class T2>
class Date
{
public:
Date(){ cout<<"Date<T1,T2>"<<endl; }
private:
T1 __d1;
T2 _ d2;
};
template<>
class Date<int,double>
{
public:
Date(){ cout<<"Date<int , double>"<<endl; }
};//全特化
template<class T>
class Date<T , double>
{
public:
Date(){ cout<<"Date<T1 , double>"<<endl; }
};//偏特化
template<class T1, class T2>
class Data<T1* , T2*>
public:
Date(){ cout<<"Date<T1* , double*>"<<endl; }
private:
T1 __d1;
T2 _ d2;
};//偏特化
2.2 函数模板的特化
template<class T>
bool less(T left,T right)
{
return left < right;
}
template<>
bool less(Date * left , Date * right)
{
return *left < *right;
}
如果需要使用函数模板处理不同类型的数据的话建议使用函数重载。
2.3模板匹配规则
如果实例化时 指定的类型是int和double的话,就不会采用模板而采用特化。实例化时能匹配全特化匹配全特化,不行在看偏特化。特化的前提一定要是原模板的。
三、模板的定义和声明
模板的定义和声明最好不要分开,就算是分开也要在一个文件中,因为模板在没有接收到具体的参数之前,是不会生成函数或者类的,如果定义和声明分开,那么编译器在编译时发现类的声明中并没有具体描述参数是什么类型的,就不会产生相应的类或者函数,所以会用法报错。
结论:模板的定义和声明不要分开写!!!
编译其首先会进行预处理操作,把头文件的所有内容展开。
接着进行编译操作(将所有的代码转化为汇编语言)在这一阶段如果有函数调用,编译器会call被调用函数的地址,如果当前文件中有该函数那么就直接使用,如果没有还需要去其他文件中寻找该函数,这一步骤会造成时间的浪费。
然后,编译器进行汇编操作将汇编指令转换为二进制指令,生成 后缀为 .o 的目标文件。
最后,编译器在多个文件中寻找之前没有找到的函数,如果还是没找到则会报错。