1. 程式人生 > >[轉]C++中模板的特化與偏特化

[轉]C++中模板的特化與偏特化

解析 匹配規則 創意 復雜 href 靈活 類模板特化 總結 行存儲

轉載自:http://hi.baidu.com/klcdyx2008/blog/item/5adbf77b79f316f90bd1873c.html

1.引言
C++中的模板分為類模板和函數模板,雖然它引進到C++標準中的時間不是很長,但是卻得到了廣泛的應用,這一點在STL中有著充分的體現。目前,STL在C++社區中得到了廣泛的關註、應用和研究。理解和掌握模板是學習、應用和研究以及擴充STL的基礎。而STL模板實例中又充斥著大量的模板特化和偏特化。


2.模板的定義
(1) 類模板

定義一個棧的類模板,它可以用來容納不同的數據類型
說明如下:
template <class T>
class stack {
private:
list* top;
public:
stack();
stack(const stack&);
~stack();
void push(T&);
T& pop();
//…
};
上述定義中,template告訴編譯器這是一個模板,尖括號中的<class T >指明模板的參數,可以有一個或多個,具體實現時由用戶指定,其中template <class T >中的關鍵字class可以用關鍵字typename來代替。
類模板的使用除了要在聲明時指明模板參數外,其余均與普通的類相同,例如:
stack<int> int_stack;
stack<char> ch_stack;
stack<string> str_stack;
int_stack.push(10);
ch_stack.push(‘z’);
str_stack.push(“c++”);
(2)函數模板


假設現在要定義一個max函數來返回同一類型(這種類型是允許比較的)兩個值的最大者.
template<class T>
T mymax(const T& t1,const T& t2)
{ return t1 < t2 ? t2 : t1; }
template <class T>的意義與類模板定義中相同。
模板函數的使用與普通非模板函數使用相同,因為模板函數的參數可以從其傳入參數中解析出來。例如:
int highest = mymax(5,10);
char c = mymax(‘a’, ’z’);
mymax(5,10)解析出模板函數參數為int, mymax(‘a’, ’z’)解析出模板函數的參數為char。


3.模板的特化
(1)類模板特化

有時為了需要,針對特定的類型,需要對模板進行特化,也就是特殊處理.例如,stack類模板針對bool類型,因為實際上bool類型只需要一個二進制位,就可以對其進行存儲,使用一個字或者一個字節都是浪費存儲空間的.
template <class T>
class stack {};
template < >
class stack<bool> { //…// };
上述定義中template < >告訴編譯器這是一個特化的模板。
(2) 函數模板的特化
看下面的例子
main()
{
int highest = mymax(5,10);
char c = mymax(‘a’, ’z’);
const char* p1 = “hello”;
const char* p2 = “world”;
const char* p = mymax(p1,p2);
}
前面兩個mymax都能返回正確的結果.而第三個卻不能,因為,此時mymax直接比較兩個指針p1 和 p2 而不是其指向的內容.
針對這種情況,當mymax函數的參數類型為const char* 時,需要特化。
template <class T>
T mymax(const T t1, const T t2)
{
return t1 < t2 ? t2 : t1;
}

template <>
const char* mymax(const char* t1,const char* t2)
{
return (strcmp(t1,t2) < 0) ? t2 : t1;
}
現在mymax(p1,p2)能夠返回正確的結果了。


4.模板的偏特化
模板的偏特化是指需要根據模板的某些但不是全部的參數進行特化
(1) 類模板的偏特化
例如c++標準庫中的類vector的定義
template <class T, class Allocator>
class vector { // … // };
template <class Allocator>
class vector<bool, Allocator> { //…//};
這個偏特化的例子中,一個參數被綁定到bool類型,而另一個參數仍未綁定需要由用戶指定。
(2) 函數模板的偏特化
嚴格的來說,函數模板並不支持偏特化,但由於可以對函數進行重載,所以可以達到類似於類模板偏特化的效果。
template <class T> void f(T); (a)
根據重載規則,對(a)進行重載
template < class T> void f(T*); (b)
如果將(a)稱為基模板,那麽(b)稱為對基模板(a)的重載,而非對(a)的偏特化。C++的標準委員會仍在對下一個版本中是否允許函數模板的偏特化進行討論。


5.模板特化時的匹配規則
(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 <class T> void f(T); // (d)
template <class T> void f(int, T, double); // (e)
template <class T> void f(T*); // (f)
template <> void f<int> (int) ; // (g)
void f(double); // (h)
bool b;
int i;
double d;
f(b); // 以 T = bool 調用 (d)
f(i,42,d) // 以 T = int 調用(e)
f(&i) ; // 以 T = int* 調用(f)
f(d); // 調用(g)

轉載自:http://blog.csdn.net/zhang810413/archive/2007/12/18/1948603.aspx

補充:

Partial Template Specialization能夠讓你在模板(Template)的所有可能的實體中特化出一組子集.
1.模板的特化(template specialization):
例如,定義如下的一個模板:
template<class Window, class Controller>
class Widget
{
... 泛化實現代碼 ...
};
然後你可以像下面那樣明確地加以特化:
template<> //註意:template後面的尖括號中不帶任何內容;
class Widget<ModalDialog, MyController>
{
... 特化實現代碼 ...
};
其中ModalDialog和MyController是你自己另外定義的類;有了這個Widget的特化定義之後,如果你以後定義了Widget<ModalDialog, MyController>對象時,編譯器就會使用上述的特化定義,如果定義了其它泛型對象,那麽編譯器就是用原本的泛化定義;這就是模板的特化.

2.Partial Template Specialization(模板偏特化)
模板特化是通過"給模板中的所有模板參數一個具體的類"的方式來實現的.而模板偏特化則是通過"給模板中的部分模板參數以具體的類,而留下剩余的模板參數仍然使用原來的泛化定義"的方式來實現的;
比如,就上面的Widget類模板的定義來說,有時候想針對任意的Window來搭配一個特定的MyController類特化Widget,這個時候就需要使用模板偏特化機制了.下面的Widget類模板就是Widget的偏特化定義:
template<class Window> //仍然使用原來的泛化定義;
class Widget<Window, MyController> //MyController是具體的類,是特化定義;
{
... 偏特化實現代碼 ...
};
這就是一個偏特化定義;一個MyController類可以搭配任意一種Window.
通常在一個類模板的偏特化定義中,你只會特化某些模板參數而留下其它泛化參數.當你在程序中具體實現上述類模板的時,編譯器會試著找出最匹配的模板定義.這個尋找過程十分復雜精細,允許你以富有創意的方式來進行偏特化.例如,假設你有一個Button類模板,它有一個模板參數,那麽,你不但可以拿任意的Window搭配特定的MyController來特化Widget,還可以拿任意Button搭配特定的MyController來偏特化Widget:
template<class ButtonArg>
class Widget<Button<ButtonArg>, MyController> //使用任意Button搭配具體的類MyContorller
{
... 偏特化實現代碼 ...
};
模板的偏特化能力很強大.當你實例化一個模板時,編譯器會把目前存在的偏特化模板和全特化模板做比較,並找出其中最合適、最匹配的實現.這樣,靈活性就很大.但是不幸的是,模板的偏特化機制不能用在函數身上,不論成員函數還是非成員函數.

註意:
1.雖然你可以全特化類模板中的成員函數,但是你不能偏特化他們;
2.你不能偏特化命名空間級別(namespace-level)的函數(non-member).最接近"命名空間級別模板函數"的偏特化機制就是函數重載,那就意味著你對"函數參數"(而非返回值類型或內部所用類型)有很精致的特化能力;
3.特化或全特化時,template後面的尖括號中不帶任何內容;

總結:
模板特化/全特化是指給每一個模板參數一個具體的類型,以具體實現這個模板,而且template後面的尖括號中不帶任何內容;
模板偏特化是指只給部分模板參數一個具體的類型,來實現這個模板;

[轉]C++中模板的特化與偏特化