模板機制為C++提供了泛型編程的方式,在減少代碼冗余的同時仍然可以提供類型安全。 特化必須在同一命名空間下進行,可以特化類模板也可以特化函數模板,但類模板可以偏特化和全特化,而函數模板只能全特化。 模板實例化時會優先匹配”模板參數”最相符的那個特化版本。
模板的聲明
類模板和函數模板的聲明方式是一樣的,在類定義/模板定義之前聲明模板參數列表。例如:
// 類模板 template <class T1, class T2> class A{ T1 data1; T2 data2; }; // 函數模板 template <class T> T max(const T lhs, const T rhs){ return lhs > rhs ? lhs : rhs; }
全特化
通過全特化一個模板,可以對一個特定參數集合自定義當前模板,類模板和函數模板都可以全特化。 全特化的模板參數列表應當是空的,並且應當給出”模板實參”列表:
// 全特化類模板 template <> class A<int, double>{ int data1; double data2; }; // 函數模板 template <> int max(const int lhs, const int rhs){ return lhs > rhs ? lhs : rhs; }
注意類模板的全特化時在類名后給出了”模板實參”,但函數模板的函數名后沒有給出”模板實參”。 這是因為編譯器根據int max(const int, const int)
的函數簽名可以推導出來它是T max(const T, const T)
的特化。
特化的歧義
上述函數模板不需指定”模板實參”是因為編譯器可以通過函數簽名來推導,但有時這一過程是有歧義的:
template <class T> void f(){ T d; } template <> void f(){ int d; }
此時編譯器不知道f()
是從f<T>()
特化來的,編譯時會有錯誤:
error: no function template matches function template specialization 'f'
這時我們便需要顯式指定”模板實參”:
template <class T> void f(){ T d; } template <> void f<int>(){ int d; }
偏特化
類似於全特化,偏特化也是為了給自定義一個參數集合的模板,但偏特化后的模板需要進一步的實例化才能形成確定的簽名。 值得注意的是函數模板不允許偏特化,這一點在Effective C++: Item 25中有更詳細的討論。 偏特化也是以template
來聲明的,需要給出剩余的”模板形參”和必要的”模板實參”。例如:
template <class T2> class A<int, T2>{ ... };
函數模板是不允許偏特化的,下面的聲明會編譯錯:
template <class T1, class T2> void f(){} template <class T2> void f<int, T2>(){}
但函數允許重載,聲明另一個函數模板即可替代偏特化的需要:
template <class T2> void f(){} // 注意:這里沒有"模板實參"
多數情況下函數模板重載就可以完成函數偏特化的需要,一個例外便是std
命名空間。 std
是一個特殊的命名空間,用戶可以特化其中的模板,但不允許添加模板(其實任何內容都是禁止添加的)。 因此在std
中添加重載函數是不允許的,在Effective C++: Item 25中給出了一個更詳細的案例。