一、非类型模板参数
模板参数分为类型形参 和非类型形参。
类型形参:出现在模板参数列表中,跟在class或typename后面的参数类型名称。
非类型形参:使用一个常量作为类(函数)模板的一个参数。

特点 :可以通过传参控制对象的属性,且只能传常量,不能传变量。
限制 :非类型模板参数只能传整型,大部分时候只能适用于一些静态的结构,比如静态数组、静态栈、静态顺序表。
不能传变量的原因:编译期间需要进行实例化,而变量无法给出确定的值。
使用:C++11里有一个新容器array会使用,是一个静态数组(fixed-size sequence containers)。
二、模板的特化
模板特化:针对某些类型进行特殊化处理,模板特化分为函数模板特化和类模板特化。特化不能单独存在,必须得先有原模板。
2.1 函数模板特化
函数模板特化的步骤:
cpp
1.必须得有一个基础的函数模板。
2.特化的函数模板关键字template后面必须得接一对<>。
3.函数名后接一对<>,<>内指定需要特化的类型。
4.特化的函数形参表必须要跟模板函数的基础参数类型完全相同,不同的话编译器会报错。
cpp
//原函数模板
template<class T>
T Add(const T& x, const T& y)
{
return x + y;
}
//特化的函数
template<>
int Add<int>(const int& x, const int& y)
{
return x + y;
}
特殊的栗子:const是修饰&的所以特化时int*需要放在const前面。
cpp
//原函数模板
template<class T>
bool Less(const T& x, const T& y)
{
return x < y;
}
//特化函数
template<>
bool Less<int*>(int* const& x, int* const& y)
{
return *x < *y;
}
注:函数模板不建议特化,如果需要处理特定类型的函数可以直接给出。
2.2 类模板特化
类模板的特化分为全特化 和偏特化。
1.全特化
全特化就是将每一个类模板参数进行确定。
cpp
//原模板
template<class T1,class T2>
class Date
{
public:
Date()
{
cout << "Date<T1,T2>" << endl;
}
private:
T1 _d1;
T2 _d2;
};
//特化的类模板
template<>
class Date<int, char>
{
public:
Date()
{
cout << "Date<int,char>" << endl;
}
private:
int _d1;
char _d2;
};
2.偏特化
偏特化分为部分特化 和对模板参数的进一步限制。
部分特化:
cpp
//部分特化,对T1特化
template<class T2>
class Date<double,T2>
{
public:
Date()
{
cout << "Date<double,T2>" << endl;
}
private:
int _d1;
char _d2;
};
//对T2特化
template<class T1>
class Date<T1,double>
{
public:
Date()
{
cout << "Date<T1,double>" << endl;
}
private:
int _d1;
char _d2;
};
对模板参数的进一步限制:进一步的限制条件有*和&。
cpp
//对模板参数的进一步限制
template<class T1,class T2>
class Date<T1*, T2*>
{
public:
Date()
{
cout << "Date<T1*,T2*>" << endl;
}
private:
int _d1;
char _d2;
};
template<class T1, class T2>
class Date<T1&, T2&>
{
public:
Date()
{
cout << "Date<T1&,T2&>" << endl;
}
private:
int _d1;
char _d2;
};
2.3 类模板特化的调用规则
实例化会按照最匹配原则进行调用。优先级是全特化>偏特化>原模板。

三、模板分离编译
分离编译:一个项目由若干个源文件共同实现,而每个源文件单独编译成目标文件,最后将所有的目标文件进行链接生成一个可执行文件,这个过程就是分离编译。
若模板的声明和定义在两个不同的文件中,例如声明在.h文件中,定义在.cpp文件中,在编译期间会报错。
模板不支持分离编译的原因:cpp文件中的模板函数在编译期间不会进行实例化,无法产生函数地址。
解决方法:
1.显示实例化(可用但不全面)
cpp
#函数模板的显示实例化
template
类型 函数名<类型>(类型 形参,类型 形参);
cpp
#Stack.h
#include<iostream>
using namespace std;
//声明
template<class T>
T Add(const T& x, const T& y);
cpp
#Stack.cpp
#include"Stack.h"
//显示实例化
template
int Add<int>(const int& x, const int& y);
//定义
template<class T>
T Add(const T& x, const T& y)
{
cout << "T Add(const T& x, const T& y)" << endl;
return x + y;
}
cpp
#类模板的显示实例化
template
返回值 类型::函数名(类型 形参)
template
返回值 类名<显示类型>::函数名(类型 形参)
cpp
#Stack.h
#include<iostream>
using namespace std;
//声明
template<class T>
class Stack
{
public:
void push(const T& val);
void pop();
private:
T* _a;
int _capacity;
int _size;
};
cpp
#Stack.cpp
#include"Stack.h"
//显示实例化
template
void Stack<int>::push(const int& val);
template
void Stack<int>::pop();
//定义
template<class T>
void Stack<T>::push(const T& val)
{
cout << "void push(const T& val)" << endl;
}
template<class T>
void Stack<T>::pop()
{
cout << "void pop()" << endl;
}
2.只在同一个文件中对模板进行声明和定义分离,.hpp文件是大概率包含模板声明和定义的文件。
如果模板出现错误时错误可能或比较多和杂乱,看第一个错误会更好。
模板文件分离编译扩展阅读: