首頁 > 軟體

C++模板的特化超詳細精講

2022-08-03 18:04:41

一、泛型程式設計

我們前面已經學過函數的過載,實現了在函數名相同的情況下,實現不同的功能!

例如:

void Swap(int& left, int& right)
{
	int temp = left;
	left = right;
	right = temp;
}
void Swap(double& left, double& right)
{
	double temp = left;
	left = right;
	right = temp;
}
void Swap(char& left, char& right)
{
	char temp = left;
	left = right;
	right = temp;
}

利用函數過載雖然實現了“通用”的交換函數,但是會有以下問題:

  • 過載的函數僅僅是型別不同,程式碼複用率低,只要有新的型別時,有需要使用者自己增加新的過載函數。
  • 程式碼的可維護性低,一個出錯可能所有的過載都出錯。

⭐️⭐️⭐️:在C++中是否存在一個模具,通過給這個模具中填充不同的材料(型別),來獲得不同的鑄件(即生成具體的程式碼)。

泛型程式設計:編寫與型別無關的通用程式碼,是程式碼複用的一種的手段。模板是泛型程式設計的基礎。

二、函數模板

2.1、函數模板的概念

函數模板代表了一個函數家族,該函數模板與型別無關,在使用時被引數化,根據實參型別產生函數的特定型別版本。

2.2、函數模板的格式

template<typename T1, typename T2,…,typename Tn>

返回值型別 函數名(參數列){}

template<typename T>
void Swap(T& left, T& right)
{
	T temp = left;
	left = right;
	right = temp;
}

⚠️注意:typename 是用來定義函數模板引數關鍵字的,也可以用class,但是不可以用struct。

2.3、函數模板的原理

函數模板我們可以理解為一個藍圖,它本身並不是函數,在編譯器編譯階段根據傳入的實參型別來推演生成對應型別的函數以供呼叫,也叫做函數模板的範例化。

2.4、函數模板的範例化

用不同型別的物件使用函數模板時,稱為函數模板的範例化。模板引數範例化分為隱式範例化和顯示範例化。

2.4.1、隱式範例化

讓編譯器根據實參推演模板引數的實際型別。

template<class T>
T Add(const T& left, const T& right)
{
	return left + right;
}
int main(){
	int a1 = 10, a2 = 20;
	double d1 = 10.0, d2 = 20.0;
	Add(a1, a2);
	Add(d1, d2);
	//Add(a1, d1);
	Add(a1, (int)d1);
	system("pause");
	return 0;
}

2.4.2、顯示範例化

讓函數名後的<>中指定模板引數的實際型別。

int main(){
	int a = 10;
	double b = 20.0;
	//Add(a, (int)b);//隱式
	Add<int>(a, b);//顯示
	system("pause");
	return 0;
}

Add(a, b);目的在於如果型別不匹配,編譯器會嘗試進行隱式型別轉換,如果無法轉換成功編譯器將會報錯。

三、類別範本

3.1、類別範本的定義格式

template<typename T1, typename T2,…,typename Tn>

class 類別範本名

{

//類內成員

};

3.1、類別範本的範例化

類別範本範例化與函數模板範例化不同,類別範本範例化傳的是型別,而函數模板範例化傳的是物件,類別範本範例化需要在類別範本名字後面跟上<>,然後將範例化型別放在<>中即可!

vector<int> s1;

⚠️:與普通類不同,這裡vector是類名,而vector才是型別。

四、模板的特化

4.1、概念

通常情況下,我們使用模板可以實現一些與型別無關的程式碼,但是對於一些特殊型別可能會得到一些錯誤的結果,需要特殊處理。

template<class T>
bool Less(T left, T right){
	return left < right;
}
//特化
template<>
bool Less<Date*>(Date* p1, Date* p2)
{
	cout << "呼叫的模板函數的特化" << endl;
	return *p1 < *p2;
}
//只要是指標  都可以呼叫這個,這個思想很重要
//偏特化的帶限制條件(指標型別可調)
template<class T>
bool Less(T* left, T* right)
{
	cout << "呼叫的函數模板" << endl;
	return *left < *right;
}

程式碼解釋:當我們在實參部分傳的是Date型指標的時候,如果不特化處理,結果會出錯。

上述就是在原模板的基礎上針對特殊型別所進行特殊化的實現方式。模板特化中分為函數模板特化和類別範本特化。

4.2、函數模板特化步驟

  • 必須要先有一個基礎的函數模板。
  • 關鍵字template後面接一隊空的<>。
  • 函數名後跟一對<>,尖括號中指定需要特化的型別。
  • 函數形參表:必須要和模板函數的基礎引數型別完全相同,如果不同編譯器可能會報一些奇怪的錯誤。
template<class T>
bool Less(T left, T right){
	return left < right;
}
//特化
template<>
bool Less<Date*>(Date* p1, Date* p2)
{
	cout << "呼叫的模板函數的特化" << endl;
	return *p1 < *p2;
}

4.3、類別範本的特化

4.3.1、全特化

全特化就是將模板引數中所有的引數都確定化

//類別範本
template<class T1, class T2>
class data
{
public:
	data(T1 a, T2 b)
		:_a(a)
		, _b(b)
	{
		cout << "data<T1, T2>" << endl;
	}
private:
	T1 _a;
	T2 _b;
};
//全特化
template<>
class data <int, char>
{
public:
	data(int a, char b)
		:_a(a)
		, _b(b)
	{
		cout << "全特化data<int, char>" << endl;
	}
private:
	int _a;
	char _b;
};
int main()
{
	data<int, int> s1(1, 2);
	data<int, char> s2(1, 'a');
}

4.3.2、偏特化

偏特化:任何針對模板引數進一步進行條件限制設計的特化版本。比如對一下類別範本:

//類別範本
template<class T1, class T2>
class data{
public:
	data(T1 a, T2 b)
		:_a(a)
		, _b(b)
	{
		cout << "data<T1, T2>" << endl;
	}
private:
	T1 _a;
	T2 _b;
};

⭐️⭐️⭐️偏特化有以下兩種變現方式:

部分特化:將模板引數中的一部分引數特化

//半特化
// 1、將模板引數類表中的一部分引數特化。
template<class T1>
class data<T1, char> {
public:
	data(T1 a, char b)
		:_a(a)
		, _b(b)
	{
		cout << "偏特化data<T1, char>" << endl;
	}
private:
	T1 _a;
	char _b;
};

引數進一步限制:偏特化並不僅僅是指特化部分引數,而是針對模板引數更進一步的條件限制所設計出來的特化版本。

// 2、偏特化並不僅僅是指特化部分引數,而是針對模板引數更進一步的條件限制所設計出來的一個特化版本。
template<class T1, class T2>
class data <T1*, T2* > {
public:
	data(T1 a, char b)
		:_a(a)
		, _b(b)
	{
		cout << "Data<T1*, T2*>" << endl;
	}
private:
	T1 _a;
	T2 _b;
};
template<class T1, class T2>
class data<T1&, T2&>
{
public:
	data(const T1& a, const T2& b)
		:_a(a)
		, _b(b)
	{ cout << "Data<T1&, T2&>" << endl; }
private:
	const T1& _a;
	const T2& _b;
};
int main(){
	data<char*, char*> s6(1,2);
	data<int&, int&> s7(1, 2);
	system("pause");
	return 0;
}

程式碼解釋:

  • 第一個是兩個引數特例化為指標型別(只要傳的是指標,就走對應的)。
  • 第二個是兩個引數特例化為參照型別。

到此這篇關於C++模板的特化超詳細精講的文章就介紹到這了,更多相關C++模板特化內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com