目錄
最近在看智能指針的源碼,發現其中用了很多模板特例化,本文就來總結一下,什麼是模板特例化。
特例化,顧名思義:特殊實例化,將函數模板或類模板實例化爲特殊的類型,通過模板特例化可以定製在特定模板參數下的函數模板/類模板實現,或者禁用特定模板的函數模板/類模板。
函數模板特例化
既然是特例化,那麼自然就需要有一個“原型”,現在假設一個原型模板函數compare,用來比較傳入的兩個參數的大小,如下所示:
template<typename T>
bool compare(T param1,T param2)
{
return param1 < param2;
}
通過這個模板函數,可以傳入任意類型的兩個參數,如compare(1,2); compare(1.1,2.1);
那要是compare("hello","hey");呢?傳入的兩個參數類型爲const char *類型的指針,這種情況如果還只是比較兩個指針的大小,顯然是不行的,應當使用strcmp來比較。所以,當模板參數類型爲const char *類型時,這就需要進行“特例化”了,特例化compare如下所示:
template<>
bool compare(const char * str, const char * str2)
{
cout << (str < str2) << endl;
return strcmp(str, str2) < 0;
}
由於前面只有一個模板參數T,特例化T後就沒有模板參數了,所以在特例化模板函數中template 參數爲空。這就相當於把原模板進行T = const char *的實例化,當T爲const char *的時候就直接調用特例化的compare。需要注意的是,這裏的特例化模板函數並不是模板重載,它一樣是匹配原模板,只不過在T爲const char *時使用特例compare。
上面是模板參數全部特例化的例子,也可以進行部分特例化,如下所示:
template<typename T1, typename T2>
void fun(T1 param1, T2 param2) //原模板
{
cout << "orginal template" << endl;
}
template<typename T>
void fun(T param1, int param2) //第二個參數爲Int時的特例化模板
{
cout << "special template" << endl;
}
int main
{
fun(1,1.0); //使用原模板函數
fun(1,1); //使用特例化模板函數
return 0;
}
特例化的一個好處就是可以在不能/不希望使用某些模板參數下的模板時,對其進行禁用。比如說上面的fun函數,我不希望第二個參數爲int,那麼就可以禁用這種特例化模板,這樣當傳入的第二個實參爲int型時,編譯器就會報錯。
template<typename T1, typename T2>
void fun(T1 param1, T2 param2) //原模板
{
cout << "orginal template" << endl;
}
template<typename T>
void fun(T param1, int param2) = delete ; //禁用第二個參數爲Int時的特例化模板
/* 也可以只聲明不定義
template<typename T>
void fun(T param1, int param2);
*/
int main
{
fun(1,1.0); //使用原模板函數
fun(1,1); //報錯,編譯器不通過
return 0;
}
在禁用特例化的情況下,fun(1,1)也沒有去調用原模板函數,這也說明了特例化並非重載(如果是重載的話,那麼就會直接調用第一個模板函數)。
類模板特例化
現在假定一個模板類A,接收兩個模板參數,第二個模板參數爲bool類型,現在將第二個模板參數爲true進行特例化,如下所示:
template<class T,bool b> //原模板
class My
{
public:
void fun()
{
cout << "original" << endl;
}
};
template<class T> //特例化第二個參數爲true時相應的模板
class My < T, true >
{
public:
void fun()
{
cout << "special" << endl;
}
};
My<double,false> m;
m.fun(); //使用原模板 打印“original”
My<int,true>n;
n.fun(); //使用特例化模板 打印“speacial”
上面這個例子其實就是unique_ptr源碼中的一部分,參考_Unique_ptr_base模板特例化 。
這只是特例化了一個模板參數,當然也可以把所有參數都特例化,在上面程序的基礎上再加上如下代碼:
template<> //特例化兩個模板參數
class My < int, true >
{
public:
void fun()
{
cout << "special special" << endl;
}
};
這就相當於把原模板中的兩個參數都進行了特例化,需要注意的是,此時如果再使用n.fun(),那麼打印的結果則是“special special”,可以看到,模板實例會優先匹配特例化程度更高的模板。
和禁用某種模板下的函數模板相同,也可以禁用某種模板下的類模板。比如說我希望禁用第二個模板參數爲true的模板,那麼就可以對其進行不完整定義,如下所示:
template<class T> //特例化第二個參數爲true時相應的模板
class My < T, true >; //不完整定義
My<int,true>n; //編譯出錯