《C++ 模板的偏特化與全特化》
Exceptional C++ Style(Herb Sutter).中文版(劉未鵬).pdf
第7條 為什麼不特化函式模板有一個概念,我以前對偏特化不是特別清晰明瞭,模板分成類模板和函式模板,我原來是一致看待的。但是事實上,函式模板能不能偏特化,只能過載,每個看個偏特化的函式模板其實都是過載。 函式模板並不參加過載決議,只有當主模板被選中,才有可能去選擇某個特化版本。 而且在選擇主模板的時候,編譯器並不關心特化模板。所以,如果你確實需要某個特化版本,那麼寫成普通函式參與過載決議就可以了。過載決議是編譯器的消耗,對執行期沒有影響。應當避免函式模板的特化,因為普通函式就可以替代。 如果你需要提供介面給使用者來具體實現,那麼使用Sutter的辦法。函式模板寫成一個孤立的、永遠不被特化的版本。其實現包含在一個包含靜態函式的類模板中。這樣特化、偏特化都沒有問題。 記住,特化模板函式不是一個好的選擇。
老實說,這兩個問題的真正答案可能是 “因為”。
您可以專門化成員函式模板,它必須在類之外,並且不能使用static
關鍵字:
struct Myclass {
template <class T>
static T func() {
T obj{};
return obj;
}
};
template <>
int Myclass::func<int>() { return 42; }
兩者都是語法上的原因。這只是你必須記住的事情之一。
https://www.cnblogs.com/dracohan/p/3401660.html
複雜。。沒看懂。。
C++ 模板的偏特化與全特化
Template 中的 class 和 typename
template<class T> class Test;
template<typename T> class Test;
在模板引入 C++ 後最初定義模板的方式是用關鍵字class
,表明緊跟在後面的符號是一個型別,後來為了避免在 class 在定義類的時候帶來混淆,又引入了typename
關鍵字。
在模板定義語法中關鍵字 class 與 typename 的作用完全一樣,但是在使用巢狀依賴型別 (nested depended name) 時只能使用typename
typedef typename T::NestType NT;
這個時候 typename 的作用就是告訴編譯器,typename 後面的字串為一個型別名稱,而不是成員函式或者成員變數,這個時候如果前面沒有 typename,編譯器沒有任何辦法知道 T::NestType 是一個型別還是一個成員名稱 (靜態資料成員或者靜態函式),所以編譯不能夠通過。
non-type template 引數
template<class T, class Ref, size_t BufSiz = 0>
class template 擁有 non-type template 引數
模板的宣告
// 類模板
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; }
偏特化
類似於全特化,偏特化也是為了給自定義一個引數集合的模板,但偏特化後的模板需要進一步的例項化才能形成確定的簽名,但值得注意的是函式模板不允許偏特化。偏特化也是以 template 來宣告的,需要給出剩餘的” 模板形參” 和必要的” 模板實參”。例如:
template <class T2>
class A<int, T2>{
...
};
所謂偏特化並不一定是對 template 引數的 T1,T2 指定某個引數值,其意思是提供另一份 template 的定義式,而其本身仍為 templatized。
template<typename T>
class c{...}; // 這個泛化版本允許接受T為任何型別
template<typename T>
class c<T*> {...};
// 這個特化版本僅適用於“T為原生指標”的情況
// “T為原生指標”便是“T為任何型別”的一個更進一步的條件限制
函式模板是不允許偏特化的,下面的宣告會編譯錯:
template <class T1, class T2>
void f(){}
template <class T2>
void f<int, T2>(){}
但函式允許過載,宣告另一個函式模板即可替代偏特化的需要:
template <class T2>
void f(){} // 注意:這裡沒有"模板實參"