c++---模板(下)

  • 非類型模板參數
  • 類模板的特化
  • 類型萃取
  • 模板的分離編譯
  1. 非類型模板參數
    模板參數分類
  • 類型形參:出現在模板參數列表中,跟在class或者typename之後的參數類型名稱。
  • 非類型形參:就是用常量作爲類(函數)的一個參數,在類(函數)模板中可將該參數當成常量來使用
namespace fw{
	template<class T, size_t  N = 10>
	class array{
	public:
			T& operator[](size_t index) const {
					return _array[index];
			}
			const T& operator[](size_t index)const{
					return _array[index];
			}
			size_t size() const{
					return _size;
			}
			bool empty() const {
					return 0 == _size;
			}
private:
			T _array[N];
			size_t  _size;
	}
}

就是在模板中我們可以使用非類型模板參數,就像我們上面程序中的N就是模板類的一個參數,但是是一個size_t 顯式定義出來的。
注意:

  • 浮點數,類對象以及字符串是不允許作爲非類型模板參數。
  • 非類型的模板參數必須在編譯器就能確認結果。也就是我們需要給予初始值。
  1. 模板的特化
    2.1概念
    通常情況下,使用模板可以實現一些與類型無關的代碼,但是對於一些特殊類 型的可能會得到一些錯誤的結果。
template<class T>
bool IsEqual(T& left,T& right){
		return left == right;
}
void test(){
		char* p1 = "hello";
		char* p2 = "world";
		if(IsEqual(p1,p2)){
				cout<<p1<<endl;
		}else{
				cout<<p2<<endl;
		}
}

在這裏插入圖片描述
此時我們傳遞的是一個指針類型,如果我們執行函數的話比較的是兩個地址是否相同,我們的原本意願是比較內容是不是相同,此時我們需要對模板進行特化。即在原模板類的基礎上,針對特殊鎖進行特殊化的實現的方式。模板特化分爲函數模板特化類模板特化

2.2 函數模板的特化
步驟:

  • 必須要先有一個基礎的函數模板
  • 關鍵字template後接一對空的尖括號
  • 函數名跟一對尖括號,尖括號中制定需要特化的類型
  • 函數形參表:必須要和模板函數的基礎參數類型完全相同,如果不同編譯器可能會報一些奇怪的錯誤。
template <>
bool IsEqual<char*>(char*& left,char*& right){
		if(strcmp(left,right)>0){
				return true;
		}
		return false;
}

注意:一般情況下如果函數模板遇到不能處理或者處理有誤的類型,爲了實現簡單通常都是將該函數直接給出。

bool IsEqual(char*& left,char*& right){
		if(strcmp(left,right)>0){
				return true;
		}
		return false;
}

2.3 類模板特化
全特化即是將模板類表中所有的參數都確定化

template <class T1,class T2>
class Data { 
public:    Data() {
		cout<<"Data<T1, T2>" <<endl;
} 
private:    T1 _d1;
			    T2 _d2; 
};
 
template<>
class Data<int, char> { 
		public:    Data() {
		cout<<"Data<int, char>" <<endl;
}
 private:    
 		T1 _d1;   
 		T2 _d2;
};
 
void TestVector() {    
		Data<int, int> d1;    
		Data<int, char> d2; 
}

偏特化
任何針對模板參數進一步進行條件限制設計的特化版本,

template<class T1, class T2> 
		class Data {
		public:   
		 		Data() {
		 		cout<<"Data<T1, T2>" <<endl;
		 		}
		private:   
				 T1 _d1;    
				 T2 _d2;
 };

偏特化的變現形式:

  • 部分特化
    將模板參數類表中的一部分參數特化
    就是在我們設計的時候給定一部分的參數
    template<class T1> 
    class Data<T1, int>
  • 參數進一步的限制
    偏特化並不僅僅是指特化部分參數,而是針對模板參數更進一步的條件限制所設計出來的一個特化版本。
    兩個參數特化爲指針類型:
template<class T, class T1>
class A<T*,T1*>{
public:
	A(){
		cout << "A<T*,T1*>" << endl;
	}
private:
	T = _d1;
	T1 = _d2;
};

兩個參數特化爲引用類型

template <typename T1, typename T2>
class Data <T1&, T2&> {
public:
	Data(const T1& d1, const T2& d2) :
		_d1(d1), _d2(d2){
		cout << "Data<T1&, T2&>" << endl
			;
	}
private:
	const T1 & _d1;
	const T2 & _d2;
};
  1. 類模板特化的應用值類型萃取
    當我們使用函數模板實現一個memcpy函數的時候
template<class T> 
void Copy(T* dst, const T* src, size_t size)
{ 
	memcpy(dst, src, sizeof(T)*size);
}

雖然這是對memcpy函數,但是並不是對所有的類型都能成功的進行拷貝,當我們拷貝自定義類型對象就可能會出錯,因爲自定義類型對象可能涉及的深拷貝( 比如string),而memcpy屬於淺拷貝,如果對象中涉及到資源管理,就只能使用賦值。
c++中只要區分去對象,最內置類型和自定義類型進行分開拷貝,對於自定義類型使用賦值操作,對於內置類型就是用memcpy進行拷貝就可以實現。
類型萃取:就是爲了將內置類型與定義類型區分開。
可以使用我們上面講解的特化來實現。
定義兩個類來表示自定義類型和內置類型。

// 代表內置類型 
struct TrueType {   
	static bool Get(){
		return true;
	}
};
 
// 代表自定義類型 
struct FalseType {     
	static bool Get(){
		return false;
	}
};

給出以下類模板

template<class T>
struct TypeTraits{
	typedef FalseType IsPODType;
}

將上述的類模板進行特化
比如我們的char和int類型。

template<> 
struct TypeTraits<char> {
	typedef TrueType     IsPODType;
};
template<> 
struct TypeTraits<int> { 
	typedef TrueType     IsPODType; 
};

將所有內置類型都進行特化。此時我們我們調用

void Copy(T* dst, const T* src, size_t size) {
	if (TypeTraits<T>::IsPODType::Get())
		memcpy(dst, src, sizeof(T)*size);   
	else { 
		for (size_t i = 0; i < size; ++i)      
			dst[i] = src[i]; 
	}
}

就可以根據傳遞的值T判斷類型。

  1. 模板分離編譯
    分離編譯:一個程序(項目)由若干個源文件共同實現,而每個源文件單獨編譯生成目標文件,最後將所有目標文件鏈 接起來形成單一的可執行文件的過程稱爲分離編譯模式。
    在模板中不支持分離編譯。必須聲明和定義在一起。原因
    編譯:需要對程序按照語言特性進行詞法,語法,語義分析,錯誤檢查無誤後生成彙編代碼,注意頭文件不參與編譯,編譯器對工程中的多個源文件是分離開單獨編譯的。
    鏈接:將多個obj文件合成一個文件,並處理沒有解決的地址問題。
    假如使用了分離編譯,在我們函數或者類實現的時候沒有對模板進行實例化,如果是函數不會生成具體的函數,在鏈接的時候因爲沒有生成函數的具體代碼,因此在鏈接的時候鏈接不到,此時就會報錯。如果我們在聲明的時候就定義出函數就直接生成函數。不會存在鏈接的問題。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章