天天看點

C++模闆進階C++模闆進階

C++模闆進階

# C++模闆初階

1.非類型模闆參數

template<class T, size_t N>
class Array
{
    private:
	T arr[N];
};
           
C++模闆進階C++模闆進階

模闆參數分為類型參數和非類型參數,如上述所述代碼就是非類型模闆參數

非類型參數:出現在模闆的參數類表中,更在class或者typename之類的參數類型名稱

非類型參數就是作為一個類(函數)的參數,在模闆中該參數可以被當作常量來使用

比如庫裡面的Array就是使用非類型模闆參數來完成的
C++模闆進階C++模闆進階
當然,非類型模闆也可以有預設的預設值
#include<iostream>
using namespace std;
namespace hello
{
	template<class T, size_t N=100>
	class Array
	{
	private:
		T arr[N];
	};
}
int main()
{
	hello::Array<int>arr;
	
	return 0;
}
           
C++模闆進階C++模闆進階

注意:

1.浮點數,類對象以及字元串是不容許作為非類型模闆參數的

2.非類型的模闆參數必須在編譯器就能确認結果

2.模闆的特化

在某些情況下,使用模闆可能會出現一些錯誤的結果,例如下面對兩個字元串的比較
//例如利用模闆來比較兩個字元串是否相等
#include<iostream>
using namespace std;
template<class T>
bool Is_Same(T& left, T& right)
{
	return left == right;
}
int main()
{
	int a = 10;
	int b = 10;
	cout << Is_Same<int>(a,b) << endl;//結果為1
	char str1[] = "hello";
	char str2[] = "hello";
	cout << Is_Same(str1, str2) << endl;//結果為0
	return 0;
}
           
C++模闆進階C++模闆進階

上面程式運作時會分别列印1,0,這說明我們在比較str1和str2這兩個字元串時出現了錯誤,為什麼會出現str1和str2不相等的情況呢?

數組名是數組首元素的位址,是一個指針,在模闆參數進行執行個體化的時候,模闆參數T會被替換成一個char*類型的指針,而這兩個字元數組都是位于棧區的,二者位址不相同,是以會傳回false

而這種特殊情況就需要對模闆進行特化,即:在原模版的基礎上,針對特殊類型所進行特殊化的實作方式,分為函數模闆特化和類模闆特化

1.函數模闆特化

步驟:

1.首先必須現有一個基礎的函數模闆

2.template後面接一對尖括号<>

3.函數名後面跟一對健康括号,尖括号内指定需要特化的類型

4.函數形參清單:必須要和函數模闆的基礎參數類型完全相同,如果編譯器不同可能報一些奇怪的錯誤

下面,我們對上面哪個求兩個字元串是否相等做一些改變:
//方法一:直接重新定義一個同名函數,裡面參數寫出char*,當我們執行Is_Same函數時,編譯器首先會在非模闆函數中查找是否有能夠進行比對的,如果參數比對直接執行該非模闆函數,不比對則在模闆函數中尋找
#include<iostream>
using namespace std;
template<class T>
bool Is_Same(T& left, T& right)
{
	return left == right;
}

//重新定義一個函數判斷兩個字元串是否相等
bool Is_Same(char* left,char*  right)
{
	if (strcmp(left, right) == 0)
		return true;
	return false;
}
int main()
{
	int a = 10;
	int b = 10;
	cout << Is_Same<int>(a,b) << endl;
	char str1[] = "hello";
	char str2[] = "hello";
	cout << Is_Same(str1, str2) << endl;
	return 0;
}
           
C++模闆進階C++模闆進階
//方法二:按照上面所說的函數模闆的特化重新寫一個函數模闆的特化
template<>
bool Is_Same<const char*const>(const char* const &left, const char* const &right)
{
	if (strcmp(left, right) == 0)
		return true;
	return false;
}
           

2.類模闆的特化

//最簡單的類模闆
template<class T1, class T2>
class A
{
private:
	T1 a;
	T2 b;
};
           

1.全特化

全特化即将模闆參數清單的所有參數都進行确定
//例如:
//在A<int,int>下我們可以執行自己想要做的是
//在A<int,double>下完成另外一件事
#include<iostream>
using namespace std;
template<class T1,class T2>
class A
{
public:
	A()
	{
		cout << "A<T1,T2>" << endl;
	}
};
template<>
class A<int, int>
{
public:
	A()
	{
		cout << "A<int ,int>" << endl;
	}
};
template<>
class A<int, double>
{
public:
	A()
	{
		cout << "A<int,double>" << endl;
	}
};

int main()
{
	A<int, int>a;
	A<int, double>b;
	A<char, int>c;
	return 0;
}
           
C++模闆進階C++模闆進階

2.偏特化

即給定類模闆一般參數
1.部分特化:将模闆參數清單中的一部分參數進行特化
class A<T1,int>
{
public:
	A()
	{
		cout << "A<T1,int>" << endl;
	}
};
           
2.參數進一步限制
偏特化不僅僅使之特化部分參數,而是正對模闆參數更近一步的條件限制所設計出來的一個特化版本
//兩個參數偏特化為指針類型
template<class T1,class T2>
class A<T1*,T2*>
{
public:
	A()
	{
		cout << "A<T1*,T2*>" << endl;
	}
};
//兩個參數偏特化為引用類型
template<class T1, class T2>
class A<T1& ,T2&>
{
public:
	A()
	{
		cout << "A<T1&,T2&>" << endl;
	}
};
//一個參數偏特化為引用,另外一個參數偏特化為指針
template<class T1,class T2>
class A<T1&,T2*>
{
public:
	A()
	{
		cout << "A<T1&,T2*>" << endl;
	}
};
           
C++模闆進階C++模闆進階

3.模闆的分離編譯

1.模闆的分離編譯即test.h中寫類模闆的聲明,test.cpp中寫類模闆的實作,main.cpp中寫主要函數功能
-------test.h---------
模闆的定義


--------test.cpp-------
模闆的實作



--------main.cpp--------
模闆的調用
           
2.在書寫模闆類時不能采取這種方式,會出現連結錯誤
C++模闆進階C++模闆進階

一個c/c++程式的執行大概遵守以上圖檔的過程,經過預處理,編譯,彙編,連結,最終形成可執行程式

test.h中放函數的聲明,test.cpp中放函數的實作,main.c中調用函數,經過預處理,編譯,彙編之後會生成test.o和main.o,當調用這個模闆函數時,發現目前隻有函數的聲明,則回去test.o的符号表中去找,因為模闆隻有在執行個體化時才能生成對應的代碼,符号表裡面沒有該模闆函數的位址,就會出現連結錯誤

3.解決方法

1.将聲明和定義放到檔案"xxx.hpp"裡面或者xxx.h中

2.模闆定義的位置顯式執行個體化

ps:推薦直接聲明與定義直接放在.h檔案中

歡迎大家的觀看,期待下次重逢 To Be Continued…

繼續閱讀