C++模板特化和偏特化(二)

一、函數模板

(1)函數的匹配優先級:

  • 普通函數;
  • 重載函數;
  • 普通函數模板;
  • 全特化函數模板。
  • 函數模板不允許使用偏特化,如有需求,改成模板函數的重載。

2)函數模板特化

      函數模板特化主要的用途都是對於特定的類型,指定特定的處理方式。函數模板特化的意義在於如果有與實參更加匹配的特例化版本,編譯器將會選擇特例化版本

例:普通函數模板

template<class T>
T add(T a, T b) { 
	return a + b; 
}

int main()
{
	int a = 1, b = 2;
	std::cout << add(a, b) << std::endl;
	return 0;
}

注:當實例化函數模板時,編譯器會自動進行實參類型推導,上面類型T就被自動推導爲int類型。

 

例:特化函數模板

類文件

// 函數模板特化
class SpeMethond
{
public:
	SpeMethond() {}
	~SpeMethond() {}

	// 普通函數模板
	template<typename T>
	T add(T x, T y) {
		T s = x + y;
		cout << "普通函數模板(一):" << x << " + " << y << " = " << s << endl;
		return s;
	}

	// 全特化函數模板,必須有對應的普通函數模板版本
	template<>
	int add(int x, int y) {
		int s = x + y;
		cout << "全特化函數模板:" << x << " + " << y << " = " << s << endl;
		return s;
	}

	// 普通函數模板(二)
	template<typename T1, typename T2>
	T2	add(T1 x, T2 y) {
		cout << "普通函數模板(二):" << x << " + " << y << " = ";
		return x + y;	// 注意此處的強制轉換
	}

	// 函數模板沒有偏特化,除非重載
	//template<typename T>
	//T add(int x, T y) {
	//	cout << "普通函數模板(一):" << x << " + " << y << " = " << s << endl;
	//	return s;
	//	return x + y;
	//}

	// 重載版本,接收參數爲指針
	template<class T1>
	T1 add(T1* a, T1* b) { 
		cout << "重載版本:" << *a << " + " << *b << " = ";
		return *a + *b; 
	}
};

調用文件:

// 函數特化
int  main()
{
	SpeMethond speMeth;
	int a = 3, b = 6;
	speMeth.add(a, b);	// 調用全特化函數模板

	double m = 2.3, n = 8.2;
	speMeth.add(m, n);	// 調用普通函數模板(一)

	cout << speMeth.add(a, m) << endl;	// 調用普通函數模板(二)

	int *x = &a, *y = &b;
	cout << speMeth.add(x, y) << endl;	// 調用重載的模板

	system("pause");
	return 0;
}

輸出:

       函數模板的全特化的用法之一,比如:std命名空間不允許增添任何新的函數重載,但是可以增添模板函數的全特化。

 

二、類模板

實例化類模板必須要指定類型,編譯器無法爲類模板自動推導類型。

類模板類型:普通類模板、全特化類模板、偏特化類模板。

1、普通類模板

例:

template<class T>
class A
{
public:
	explicit A(T val) : t(val) { }

	T add(T x) { 
		return t + y; 
	}

private:
	T t;
};

注意:實例化類模板必須要指定類型,編譯器無法爲類模板自動推導類型。

 

2、全特化類模板

       類模板全特化比較好理解,跟函數模板一樣,全特化是一個實例,當編譯器匹配時會優先匹配參數一致的實例。

例:

template<>
class A<char*>		// 當用char*類型來實例化類模板A時,將會優先調用這個全特化實例
{                    
public:
	explicit A(char* val) : t(val) { }

	char* add(char* a, char* b) { 
		return strcat(a, b); 
	}

private:
	char* t;
};

 

3、偏特化類模板

       類模板的偏特化有多種形式,類模板偏特化本質上都是指定部分類型,讓偏特化版本成爲普通版本的子集,若實例化時參數類型爲指定的類型,則優先調用特例化版本。

(1)形式1

template<class T1, class T2>      // 普通版本,有兩個模板參數
class B { 
	//..... 
};

template<class T2>		// 偏特化版本,指定其中一個參數,即指定了部分類型
class B<int, T2> {		// 當實例化時的第一個參數爲int 則會優先調用這個版本
	//..... 
};	

(2)形式2

template<class T>     // 普通版本
class B { 
	//..... 
};

template<class T>	//這個偏特化版本只接收指針類型的模板實參 
class B<T* > { 
	//..... 
};

template<class T>
class B<T&> {		// 這個偏特化版本只接受引用類型的模板實參
	//..... 
};

(3)形式3

template<class T>    //普通版本
class B { ..... };

template<class T>	// 這種只接受用T實例化的vector的模板實參.也是一種偏特化
class B<vector<T>> { 
	//...... 
};

 

注意:

  • 若想讓用戶能使用特例化版本,特例化模板版本必須與普通模板定義在同一個.h頭文件中。
  • 特例化本質上是我們頂替了編譯器的工作,我們幫編譯器做了類型推導,如果特化和非特化模板同時存在時,編譯器認爲我們有更好的實現版本,所有會優先選擇特化模板的版本。
  • 全特化本質上是一個實例,而偏特化本質上還是一個模板,只是原來模板的一個子集。
  • 模板的實例化類型確定是在編譯期間。
  • 模板實例化只會實例化用到的部分,沒有用到的部分將不會被實例化。
  • 只是模板寫好了,編譯一般不會很多出錯,出錯一般會在實例化編譯之後。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章