目录
最近在看智能指针的源码,发现其中用了很多模板特例化,本文就来总结一下,什么是模板特例化。
特例化,顾名思义:特殊实例化,将函数模板或类模板实例化为特殊的类型,通过模板特例化可以定制在特定模板参数下的函数模板/类模板实现,或者禁用特定模板的函数模板/类模板。
函数模板特例化
既然是特例化,那么自然就需要有一个“原型”,现在假设一个原型模板函数compare,用来比较传入的两个参数的大小,如下所示:
template<typename T>
bool compare(T param1,T param2)
{
return param1 < param2;
}
通过这个模板函数,可以传入任意类型的两个参数,如compare(1,2); compare(1.1,2.1);
那要是compare("hello","hey");呢?传入的两个参数类型为const char *类型的指针,这种情况如果还只是比较两个指针的大小,显然是不行的,应当使用strcmp来比较。所以,当模板参数类型为const char *类型时,这就需要进行“特例化”了,特例化compare如下所示:
template<>
bool compare(const char * str, const char * str2)
{
cout << (str < str2) << endl;
return strcmp(str, str2) < 0;
}
由于前面只有一个模板参数T,特例化T后就没有模板参数了,所以在特例化模板函数中template 参数为空。这就相当于把原模板进行T = const char *的实例化,当T为const char *的时候就直接调用特例化的compare。需要注意的是,这里的特例化模板函数并不是模板重载,它一样是匹配原模板,只不过在T为const char *时使用特例compare。
上面是模板参数全部特例化的例子,也可以进行部分特例化,如下所示:
template<typename T1, typename T2>
void fun(T1 param1, T2 param2) //原模板
{
cout << "orginal template" << endl;
}
template<typename T>
void fun(T param1, int param2) //第二个参数为Int时的特例化模板
{
cout << "special template" << endl;
}
int main
{
fun(1,1.0); //使用原模板函数
fun(1,1); //使用特例化模板函数
return 0;
}
特例化的一个好处就是可以在不能/不希望使用某些模板参数下的模板时,对其进行禁用。比如说上面的fun函数,我不希望第二个参数为int,那么就可以禁用这种特例化模板,这样当传入的第二个实参为int型时,编译器就会报错。
template<typename T1, typename T2>
void fun(T1 param1, T2 param2) //原模板
{
cout << "orginal template" << endl;
}
template<typename T>
void fun(T param1, int param2) = delete ; //禁用第二个参数为Int时的特例化模板
/* 也可以只声明不定义
template<typename T>
void fun(T param1, int param2);
*/
int main
{
fun(1,1.0); //使用原模板函数
fun(1,1); //报错,编译器不通过
return 0;
}
在禁用特例化的情况下,fun(1,1)也没有去调用原模板函数,这也说明了特例化并非重载(如果是重载的话,那么就会直接调用第一个模板函数)。
类模板特例化
现在假定一个模板类A,接收两个模板参数,第二个模板参数为bool类型,现在将第二个模板参数为true进行特例化,如下所示:
template<class T,bool b> //原模板
class My
{
public:
void fun()
{
cout << "original" << endl;
}
};
template<class T> //特例化第二个参数为true时相应的模板
class My < T, true >
{
public:
void fun()
{
cout << "special" << endl;
}
};
My<double,false> m;
m.fun(); //使用原模板 打印“original”
My<int,true>n;
n.fun(); //使用特例化模板 打印“speacial”
上面这个例子其实就是unique_ptr源码中的一部分,参考_Unique_ptr_base模板特例化 。
这只是特例化了一个模板参数,当然也可以把所有参数都特例化,在上面程序的基础上再加上如下代码:
template<> //特例化两个模板参数
class My < int, true >
{
public:
void fun()
{
cout << "special special" << endl;
}
};
这就相当于把原模板中的两个参数都进行了特例化,需要注意的是,此时如果再使用n.fun(),那么打印的结果则是“special special”,可以看到,模板实例会优先匹配特例化程度更高的模板。
和禁用某种模板下的函数模板相同,也可以禁用某种模板下的类模板。比如说我希望禁用第二个模板参数为true的模板,那么就可以对其进行不完整定义,如下所示:
template<class T> //特例化第二个参数为true时相应的模板
class My < T, true >; //不完整定义
My<int,true>n; //编译出错