template之全特化和偏特化
前言
關於講過traits
萃取器的時候探討到偏特化的概念, 而在那一篇文章也沒有具體解釋偏特化是什麼, 怎麼實現, 所以可能在第一次看得時候會很莫名其妙. 所以我將偏特化放在其後講解, 為不明白的朋友做一個淺析的講解. 這裡我先聊一下全特化再聊偏特化.
全特化
全特化的模板引數列表應該是為空, 函式和類都可以實現全特化.
template<class T>
void fun(T a)
{
cout << "fun a = " << a << endl;
}
template<>
void fun(int a)
{
cout << "fun1 a = " << a << endl;
}
int main()
{
fun(3.3);
fun(3);
exit(0);
}
結果如下
這就是函式全特化, 根據傳入的引數讓編譯器自動推導引數的型別來呼叫其特殊的函式.
記住:
- 函式的全特化不是過載, 不是過載.
- 全特化的引數列表要為空, 為空
- 第二點成立是因為我們要實現一個相同的模板, 一個相同的模板
同樣, 類的全特化也是一樣的, 只要滿足上面的三點就行了.
偏特化
函式不能偏特化, 類可以偏特化.
偏特化需要在執行例項化的時候才能推導確定使用哪一個模板類. 偏特化也是以template
來宣告的,需要給出剩餘的”模板形參”和必要的”模板實參”.
template<class T>
class Point
{
public:
void Print()
{
cout << "Point" << endl;
}
};
template<class T>
class Point<const T>
{
public:
void Print()
{
cout << "const Point" << endl;
}
};
int main()
{
Point<double> b;
b.Print();
Point<const int> c;
c.Print();
exit(0);
}
以上就實現了一個關於const T
的偏特化, 這就很像traits萃取器
實現的偏特化了.
偏特化的重點 :
- 函式不能偏特化, 因為函式可以過載, 也就可以實現型別偏特化一樣的功, 而類不可以過載.
- 偏特化只是針對一些特殊的引數型別.
- 偏特化實現了類的"過載".
還有除了可以特化類模板之外, 還可以對類模板中的成員函式和普通靜態成員變數進行特化.
優先順序
上面全特化和偏特化還有一點沒有談論到, 關於優先順序. 現在我們就來看一下
// 這是上面的一個例項
fun(3.3);
fun(3);
fun(3)的函式不是呼叫template<class T> void fun(T a)
而優先呼叫的是<int>
的全特化模板.
同樣偏特化的的例子也能證明這一點
Point<double> b;
b.Print();
Point<const int> c;
c.Print();
類優先呼叫了最合適的模板.
上面就可以歸納為:
- 全特化/偏特化, 普通模板(優先順序從左到右依次減小)
現在我們再實現一個普通沒有模板的fun函式
void fun(int a) {}
如果繼續呼叫fun(3)
, 你會發現此時沒有任何輸出. 那是不是沒有實現的模板函式和類的例項會優先被呼叫呢? 確實如此.
以上就可以歸納為 :
- 全特化/偏特化, 普通模板(優先順序從左到右依次減小)
- 無模板函式優先順序最高
總結
函式只能全特化, 不能偏特化, 類既可以全特化, 也可以偏特化. 函式不能偏特化但是可以過載, 類不能進行過載.
優先順序 : 無模板函式 > 全特化/偏特化 > 普通模板
現在如果重新去看traits萃取器
應該就能理解traits
程式設計使用偏特化的意義.