最近看了看到了模板的特化,突然想起來上說的顯式具體化、隱式具體化、特化、偏特化、具體化等概念弄得頭暈腦脹,我在網上了找了好多帖子,才把概念給理清楚。
一下是我把再網上找的資料整理一下。
看著這麼多叫法,其實就是三種。
1. 顯示例項化
2. 隱式例項化
3. 特化(=具體化)、偏特化
一、例項化
1.顯示、隱式例項化
什麼是例項化:一個通過使用具體值替換模板引數,從模板產生的普通類,函式或者成員函式的過程。
顯示例項化:通過名字可見,就是清楚的表明你要例項化的型別
隱式例項化:通過編譯器自己推測判斷要例項化的型別。
比如一個模板:
1 2 3 4 5 6 7 8 |
template<class T> //函式模板實現 void swap(T &a, T &b) { T temp; temp = a; a = b; b = temp; } |
a. 顯示例項化
template void swap<int>(); // 無須給該函式重新編寫函式體,這只是個宣告
為什麼要顯示例項化?
主要是提高效率,當顯式例項化模板時,在使用模板之前,編譯器根據顯式例項化指定的型別生成模板例項,這樣就相當於本程式裡面有個一
1 2 3 4 5 6 7 |
void swap(int &a, int &b) { int temp; temp = a; a = b; b = temp; } |
這樣的話,每次需要呼叫 swap(a,b)的時候每次都重新生成該型別的程式碼,可以節省空間,也能提高效率。這就是為什麼要是顯式的例項化的原因。
b. 隱式例項化
隱式例項化指的是:在使用模板之前,編譯器不生成模板的宣告和定義例項。只有當使用模板時,編譯器才根據模板定義生成相應型別的例項。
1 2 |
int i=0, j=1; swap(i, j); //編譯器根據引數i,j的型別隱式地生成swap(int &a, int &b)的函式定義。 |
隱式例項化就是程式設計師為了省事,把型別省略讓編譯器判斷,這是一個偷懶的表現吧。
二、特化
1. 特化(=具體化)
然而通常又有一些特殊的情況,不能直接使用泛型模板展開實現,這時就需要針對某個特殊的型別或者是某一類特殊的型別,而實現一個特例模板————即模板特化
當T如果為 一個 struct型別的,它的交換就無法進行,所以我們針對這種特殊的情形,我們專門寫了一個函式,只有當T為 這種struct型別時候,才會呼叫這個特化的函式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
//對函式 #define MAXNAME 128 struct job { char name[MAXNAME]: int salary; }; template<class T> void swap(T &a, T &b ) { T temp; temp = a; a = b; b = temp; }; template void swap<int>(int &a, int & b); //顯式例項化,只需宣告 template<> void swap<job>(job &a, job &b) //顯式具體化(上面已經講過,注意與例項化區分開,必須有定義) { int salary: salary = a.salary: a.salary = b.salary; b.salary = salary; };//explicite specialization. //對類别範本: template <class T> class Arrary { private: T* ar; int l; ... };//template class declaration. template class Array<int>; //explicit instantiation. 顯式例項化 template<> class Array<job> { private: job* ar; int l; };//expicit specialization. 顯式具體化,類定義體可以不同於類别範本Array |
2. 偏特化
模板的偏特化是指需要根據模板的部分引數進行特化。
a. 類别範本的偏特化
例如c++標準庫中的類vector的定義
1 2 3 4 5 |
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)的偏特化。
這裡我就不深入的剖析偏特化了。
三、模板的匹配順序
1. 類别範本的匹配規則
例如:
1 2 3 4 |
template <class T> class vector{//…//}; // (a) 普通型 template class vector<typename> ; // (b) 的顯式例項化 template <class T> class vector<T*>{//…//}; // (c) 對指標型別特化 template <> class vector <void*>{//…//}; // (d) 對void*進行特化 |
每個型別都可以用作普通型(a)的引數,但只有指標型別才能用作(b)的引數,而只有void*才能作為(c)的引數
所以,當一個呼叫一個模板類,首先,找顯式例項化的,如果不匹配;接著,找特化的,然後,找偏特化的,最後,根據模板隱式例項化
2.函式模板的匹配規則
例如:
1 2 3 4 |
void swap(int &a, int &b){} // 普通的函式 template<> swap<int>(int &a, int &b){} // 特化的模板函式 template void swap<int>(int &a, int &b); // 顯式例項化,這個只用宣告就行 template<class T> void swap(T &a, T &b){} // 模板 |
以上書寫的順序就是模板的呼叫順序。