模板为什么要特化,因为编译器认为,对于特定的类型,如果你能对某一功能更好的实现,那么就该听你的。
模板分为类模板与函数模板,特化分为全特化与偏特化。全特化就是限定死模板实现的具体类型,偏特化就是如果这个模板有多个类型,那么只限定其中的一部分。
先看类模板:
template<typename T1, typename T2> class Test { public: Test(T1 i,T2 j):a(i),b(j){cout<<"模板类"<<endl;} private: T1 a; T2 b; }; template<> //注意此处<>中的参数为空 class Test<int , char> { public: Test(int i, char j):a(i),b(j){cout<<"全特化"<<endl;} private: int a; char b; }; template <typename T2> class Test<char, T2> { public: Test(char i, T2 j):a(i),b(j){cout<<"偏特化"<<endl;} private: char a; T2 b; };
Test<double , double> t1(0.1,0.2); Test<int , char> t2(1,'A'); Test<char, bool> t3('A',true);
而对于函数模板,却只有全特化,不能偏特化:
//模板函数 template<typename T1, typename T2> void fun(T1 a , T2 b) { cout<<"模板函数"<<endl; } //全特化 template<> void fun<int ,char >(int a, char b) { cout<<"全特化"<<endl; } //函数不存在偏特化:下面的代码是错误的 /* template<typename T2> void fun<char,T2>(char a, T2 b) { cout<<"偏特化"<<endl; } */
至于为什么函数不能偏特化,似乎不是因为语言实现不了,而是因为偏特化的功能可以通过函数的重载完成。
我们先来定义一个普通的模板类
1 template<class T> 2 struct Test 3 { 4 Test(){ cout << "模板类" << endl; } 5 };
我们再来调用一下:
1 int main() 2 { 3 Test<char> t1; 4 Test<int> t2; 5 Test<int *> t3; 6 return 0; 7 }
输出的结果1:
模板类
模板类
模板类
如果我们这样写,再增加一个模板类
1 template<class T> 2 struct Test 3 { 4 Test(){ cout << "模板类" << endl; } 5 }; 6 7 template<> 8 struct Test<int> 9 { 10 Test(){ cout << "全特化" << endl; } 11 };
再来调用一下,调用代码同上!
输出的结果2:
模板类
全特化
模板类
其中template<>开头的模板类就是全特化的模板类,根据全特化模板的定义就知道,全特化的时候没有类型参数,即template<(这里是空)>。由于全特化版本的模板类与Test<int> t2;调用时更接近,所以会用这个模板来实例化一个类。
那什么是偏特化模板类,先看如下定义:
1 template<class T> 2 struct Test 3 { 4 Test(){ cout << "模板类" << endl; } 5 }; 6 7 template<> 8 struct Test<int> 9 { 10 Test(){ cout << "全特化" << endl; } 11 }; 12 13 template<class T> 14 struct Test<T*> 15 { 16 Test(){ cout << "偏特化" << endl;} 17 };
再次调用,输出结果3:
模板类
全特化
偏特化
我们看一下,第三个模板类的定义,会发现它很特别,它既有类型参数。但又需要对其进行特化(这里是特化成指针类型),当你给的是指针类型的时候,会调用这个模板,而不是普通模板。这就是部分特化,也称偏特化。有的人可能会想到,我这样写不也行么:
template<class T*> struct Test { Test(){ cout << "型别是指针!" << endl;} };
对不起,这段代码可以通过编译,但是在你实例化一个类的时候,即Test<int *> t;的时候就编译不同过了!所以必须要用到偏特化!当然,我上面写的是偏特化的一种,当这样的时候也是偏特化:
template<class T> struct Test<int,T> { }
这个偏特化的例子中,一个参数被绑定到int类型,而另一个参数仍未绑定需要由用户指定。这也是偏特化。而且更好理解!还有一点要注意的是,我们在写全特化类模板和偏特化类模板的时候,都必须给出一个普通类模板的定义,如果不定义普通模板而只定义全特化模板或者偏特化模板编译时会报错:对不起,XXX不是一个模板!