http://blog.csdn.net/yesterday_record/article/details/7304025
很久没有看C++,在看STL源码剖析时,看到一个function template partial order(偏序模板函数)概念,一时不明白,于是在网上搜罗一下,看完了才明白了就是以前在学校看的《C++ Primer》中的模板特例化相关的概念。这里将搜到的资料整理一下。
1. 模板的特化
C++中经常为了避免重复的编码而需要使用到模板,这是C++泛型编程不可或缺的利器。然而通常又有一些特殊的情况,不能直接使用泛型模板展开实现,这时就需要针对某个特殊的类型或者是某一类特殊的类型,而实现一个特例模板————即模板特化。通常会使用到模板特化的有(应该也只能有)类模板和函数模板。
a. 类模板特化
在已有类模板
template <class T>
class stack { //...// };
定义时,可能考虑到一些特定的类型T,数据的存储可能和通用模板不一样,因而可以像如下定义一个特例化模板:
template < >
class stack<bool> { //...// };
b. 函数模板的特化
template <class T>
T max(const T t1, const T t2)
{
return t1 < t2 ? t2 : t1;
}
如上已有的模板定义可能在针对一个指针类型的参数时,工作将可能会不正常,具体到字符串指针类型时,可能就需要下面的特例化模板定义:
template < >
const char* max(const char* t1,const char* t2)
{
return (strcmp(t1,t2) < 0) ? t2 : t1;
}
这样才能使max("aaa", "bbb");的调用更如人意(通常没有人想比较两个常量字符串存储的地址的大小)。
2. 模板的偏特化
模板的偏特化是指需要根据模板的部分参数进行特化。
a. 类模板的偏特化
例如c++标准库中的类vector的定义
template <class T, class Allocator>
class vector { // … // };
template <class Allocator>
class vector<bool, Allocator> { //…//};
这个偏特化的例子中,一个参数被绑定到bool类型,而另一个参数仍需要由用户使用时指定。
b. 函数模板的偏特化
网上看到有人说:从严格意义上讲,函数模板并不支持偏特化(我对这个不是很理解),但由于可以对函数进行重载,所以可以达到类似于类模板偏特化的效果。
比如:
a) template <class T> void f(T);
根据重载规则,对a)进行重载
b) template < class T> void f(T*);
如果将a)称为基模板,那么b)称为对基模板a)的重载,而非对a)的偏特化。
3. 模板特化时的匹配规则
(1) 类模板的匹配规则
最优化的优于次特化的,即模板参数最精确匹配的具有最高的优先权
例如:
template <class T> class vector{//…//}; // (a) 普通型
template <class T> class vector<T*>{//…//}; // (b) 对指针类型特化
template <> class vector <void*>{//…//}; // (c) 对void*进行特化
每个类型都可以用作普通型(a)的参数,但只有指针类型才能用作(b)的参数,而只有void*才能作为(c)的参数
(2) 函数模板的匹配规则
非模板函数具有最高的优先权。如果不存在匹配的非模板函数的话,那么最匹配的和最特化的函数具有高优先权。
比如有如下模板:
template<typename T>
void func1(T t)
{
cout<<"func1 1"<<endl;
}
template<typename T>
void func1(T* t)
{
cout<<"func1 2"<<endl;
}
template<typename T>
void func2(T t)
{
cout<<"func2"<<endl;
}
编译器判段两个函数模板谁比谁更特化的方法是尝试,编译器自己生成内部类型A,把它分别代入函数func1的模板,比如编译器如下构造{A a}现在就有了如下两个函数
func1(a); //这个利用第一个模板代入
func1(a*);//这个利用第二个模板代入
现在编译器把这两个反向,把a代码第二个模板,把a*代入第一个模板,显然第一个模板函数是可以接受指针类型的,让T为a*就可以了;
但是第二个模板函数不能接收a,因为它的参数必须是一个指针。由此编译器知道第一个函数模板比第二个函数模板更加泛化,也就是说第二个函数模板比第一个函数模板更加特化。
好像在《C++ Primer》中有类似如下的描述:
C++中,函数模板与同名的非模板函数重载时,应遵循下列调用原则:
a. 寻找一个参数完全匹配的函数,若找到就调用它。若参数完全匹配的函数多于一个,则这个调用是一个错误的调用。
b. 寻找一个函数模板,若找到就将其实例化生成一个匹配的模板函数并调用它。
c. 若上面两条都失败,则使用函数重载的方法,通过类型转换产生参数匹配,若找到就调用它。
d. 若上面三条都失败,还没有找都匹配的函数,则这个调用是一个错误的调用。
总结:
1. 模板的特化是在已有的通用模板不再适用于一些特殊的类型参数时,而针对这些特殊的类型参数专门实现的模板。
2. 模板的偏特化是指需要根据模板的部分参数进行特化。
3. 函数调用匹配的规则是:先精确匹配类型参数,然后匹配函数模板,最后通过参数隐式类型转换进行匹配。