1. 程式人生 > >template之全特化和偏特化

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); }

結果如下

在這裡插入圖片描述

這就是函式全特化, 根據傳入的引數讓編譯器自動推導引數的型別來呼叫其特殊的函式.

記住:

  1. 函式的全特化不是過載, 不是過載.
  2. 全特化的引數列表要為空, 為空
  3. 第二點成立是因為我們要實現一個相同的模板, 一個相同的模板

同樣, 類的全特化也是一樣的, 只要滿足上面的三點就行了.

偏特化

函式不能偏特化, 類可以偏特化.

偏特化需要在執行例項化的時候才能推導確定使用哪一個模板類. 偏特化也是以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萃取器實現的偏特化了.
在這裡插入圖片描述

偏特化的重點 :

  1. 函式不能偏特化, 因為函式可以過載, 也就可以實現型別偏特化一樣的功, 而類不可以過載.
  2. 偏特化只是針對一些特殊的引數型別.
  3. 偏特化實現了類的"過載".

還有除了可以特化類模板之外, 還可以對類模板中的成員函式和普通靜態成員變數進行特化.

優先順序

上面全特化和偏特化還有一點沒有談論到, 關於優先順序. 現在我們就來看一下

// 這是上面的一個例項
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();

類優先呼叫了最合適的模板.

上面就可以歸納為:

  1. 全特化/偏特化, 普通模板(優先順序從左到右依次減小)

現在我們再實現一個普通沒有模板的fun函式

void fun(int a) {}

如果繼續呼叫fun(3), 你會發現此時沒有任何輸出. 那是不是沒有實現的模板函式和類的例項會優先被呼叫呢? 確實如此.

以上就可以歸納為 :

  1. 全特化/偏特化, 普通模板(優先順序從左到右依次減小)
  2. 無模板函式優先順序最高

總結

函式只能全特化, 不能偏特化, 類既可以全特化, 也可以偏特化. 函式不能偏特化但是可以過載, 類不能進行過載.

優先順序 : 無模板函式 > 全特化/偏特化 > 普通模板

現在如果重新去看traits萃取器應該就能理解traits程式設計使用偏特化的意義.