C++ 模版
模板是C++支持參數化多態的工具,使用模板可以使用戶為類或者函數聲明一種一般模式,使得類中的某些數據成員或者成員函數的參數、返回值取得任意類型;模板是一種對類型進行參數化的工具。
通常有兩種形式:函數模板和類模板;
函數模板:針對僅參數類型不同的函數;
類模板: 針對僅數據成員和成員函數類型不同的類。
注意:模板的聲明或定義只能在全局,命名空間或類范圍內進行。即不能在局部范圍內進行。
1、 函數模版
template <class 形參名,class 形參名,......> 返回類型 函數名(參數列表)
{
函數體
}
1、其中template和class是關見字,class可以用typename 關見字代替,在這里typename 和class沒區別,這里說名一下兩個的區別和相同:
--- 在聲明 template parameters(模板參數)時,class 和 typename 是可互換的。
--- 用 typename 去標識 nested dependent type names(嵌套依賴類型名),在 base class lists(基類列表)中或在一個 member initialization list(成員初始化列表)中作為一個 base class identifier(基類標識符)時除外,這種情況 class 和 typename是不可互換。
看看例子:
class MyArray
{
public:
typedef int LengthType;
.....
}
template<class T>
void MyMethod( T myarr )
{
typedef typename T::LengthType LengthType;
LengthType length = myarr.GetLength;
}
這個時候typename的作用就是告訴c++編譯器,typename后面的字符串為一個類型名稱,而不是成員函數或者成員變量,這個時候如果前面沒有
typename,編譯器沒有任何辦法知道T::LengthType是一個類型還是一個成員名稱(靜態數據成員或者靜態函數),所以編譯不能夠通過。
2、 <>括號中的參數叫模板形參
模板形參和函數形參很相像,模板形參一般不能為空。為空的時候后面需要具解釋這個用法特化處理。這里我們先介紹不為空的。
一但聲明了模板函數就可以用模板函數的形參名聲明類中的成員變量和成員函數,即可以在該函數中使用內置類型的地方都可以使用模板形參名。模板形參需要調用該模板函數時提供的模板實參來初始化模板形參,一旦編譯器確定了實際的模板實參類型就稱他實例化了函數模板的一個實例。
注意:對于函數模板而言不存在 h(int,int) 這樣的調用,不能在函數調用的參數中指定模板形參的類型,對函數模板的調用應使用實參推演來進行,即只能進行 h(2,3) 這樣的調用,或者int a, b; h(a,b)。
示例:
template <class T>
int compare(const T &left, const T&right)
{
std::cout <<"in template<class T>..." <<std::endl;
return (left - right);
}
2、 類模版
template<class 形參名,class 形參名,…> class 類名 // 定義
{ ... };
類模板對象的創建:比如一個模板類A,則使用類模板創建對象的方法為A<int> m;
對于類模板,模板形參的類型必須在類名后的尖括號中明確指定。比如A<2> m;用這種方法把模板形參設置為int是錯誤的。
在類模板外部定義成員函數的方法為:
template<模板形參列表> 函數返回類型 類名<模板形參名>::函數名(參數列表){函數體},
比如有兩個模板形參T1,T2的類A中含有一個void h()函數,則定義該函數的語法為:
template<class T1,class T2> void A<T1,T2>::h(){}
注意:當在類外面定義類的成員時template后面的模板形參應與要定義的類的模板形參一致。
示例:
#include <iostream>
#include <cstring>
#include <cmath>
// general version
template<class T>
class Compare
{
public:
static bool IsEqual(const T& lh, const T& rh)
{
std::cout <<"in the general class..." <<std::endl;
return lh == rh;
}
};
3、 模版形參
有三種類型的模板形參:‘類型’形參,非類型形參和模板形參。類型不是指class定義的類,通過模版定義的<typename T>這些。
1、類型形參
類型模板形參:類型形參由關見字class或typename后接說明符構成,如template<class T> void h(T a){};其中T就是一個類型形參,類型形參的名字由用戶自已確定。模板形參表示的是一個未知的類型。
注意:
對于函數模版:不能為同一個模板類型形參指定兩種不同的類型,比如template<class T>void h(T a, T b){},語句調用h(2, 3.2)將出錯。
對于類模版:聲明類對象為:A<int> a,比如template<class T>T g(T a, T b){},語句調用a.g(2, 3.2)在編譯時不會出錯,有警告;當聲明A<double> a,警告都不會有。
2、 非類型形參
1 、非類型模板形參:模板的非類型形參也就是內置類型形參,如template<class T, int a> class B{};其中int a就是非類型的模板形參。
2、 非類型形參在模板定義的內部是常量值。
3、 非類型模板的形參只能是整型,指針和引用。
4、 調用非類型模板形參的實參必須是一個常量表達式,即他必須能在編譯時計算出結果。常量表達式有:
---1> 全局變量的地址或引用,全局對象的地址或引用const類型變量是常量表達式,可以用作非類型模板形參的實參。
---2> sizeof表達式的結果是一個常量表達式,也能用作非類型模板形參的實參。
5、當模板的形參是整型時調用該模板時的實參必須是整型的,且在編譯期間是常量,比如template <class T, int a> class A{};如果有int b,這時A<int, b> m;將出錯,因為b不是常量,如果const int b,這時A<int, b> m;就是正確的,因為這時b是常量。
6、非類型形參一般不應用于函數模板中,比如有函數模板template<class T, int a> void h(T b){},若使用h(2)調用會出現無法為非類型形參a推演出參數的錯誤,對這種模板函數可以用顯示模板實參來解決,如用h<int, 3>(2)這樣就把非類型形參a設置為整數3。顯示模板實參在后面介紹。
7、 非類型模板形參的形參和實參間所允許的轉換
1、允許從數組到指針,從函數到指針的轉換。如:template <int *a> class A{}; int c[1]; A<c> m;即數組到指針的轉換
2、const修飾符的轉換。如:template<const int *a> class A{}; int b; A<&b> m; 即從int *到const int *的轉換。
3、提升轉換。如:template<int a> class A{}; const short c=2; A<c> m; 即從short到int 的提升轉換
4、整值轉換。如:template<unsigned int a> class A{}; A<3> m; 即從int 到unsigned int的轉換。
5、常規轉換。
注意: 任何局部對象,局部變量,局部對象的地址,局部變量的地址都不是一個常量表達式,都不能用作非類型模板形參的實參。全局指針類型,全局變量,全局對象也不是一個常量表達式,不能用作非類型模板形參的實參。
補充:
類模板的默認模板類型形參:
1、可以為類模板的類型形參提供默認值,但不能為函數模板的類型形參提供默認值。函數模板和類模板都可以為模板的非類型形參提供默認值。
2、類模板的類型形參默認值形式為:template<class T1, class T2=int> class A{};為第二個模板類型形參T2提供int型的默認值。
3、類模板類型形參默認值和函數的默認參數一樣,默認應該在后面,如果第一個設置默認,那么后面其他的都應該設置默認。
4、 在類模板的外部定義類中的成員時template 后的形參表應省略默認的形參類型。如: template<class T1, class T2=int> class A{public: void h();}; 外部函數 :template<class T1,class T2> void A<T1,T2>::h(){}。
4、 模版特化
特化:為已有的模板參數進行一些使其特殊化的指定,使得以前不受任何約束的模板參數,或受到特定的修飾(例如const或者變成為了指針之類,甚至是經過別的模板類包裝之后的模板類型)或完全被指定了下來。
分類:
------- 針對特化的對象不同,分為兩類:函數模板的特化和類模板的特化。
函數模板的特化:當函數模板需要對某些類型進行特化處理,稱為函數模板的特化。
類模板的特化:當類模板內需要對某些類型進行特別處理時,使用類模板的特化。
------- 特化整體上分為全特化和偏特化
全特化:全特化的類中的函數可以與模板類不一樣。
偏特化:就是模板中的模板參數沒有被全部確定,需要編譯器在編譯時進行確定。
注意: 模板函數只能全特化,沒有偏特化(以后可能有);模板類是可以全特化和偏特化的。
還是使用上面的示例:
// 一個模版函數
template <class T>
int compare(const T &left, const T&right)
{
std::cout <<"in template<class T>..." <<std::endl;
return (left - right);
}
// 一個特化的函數
template < >
int compare<const char*>(const char* left, const char* right)
{
std::cout <<"in special template< >..." <<std::endl;
return strcmp(left, right);
}
// 或者
template < >
int compare(const char* left, const char* right)
{
std::cout <<"in special template< >..." <<std::endl;
return strcmp(left, right);
}
函數模版的特化,當函數調用發現有特化后的匹配函數時,會優先調用特化的函數,而不再通過函數模版來進行實例化。
類模板的特化:與函數模板類似,當類模板內需要對某些類型進行特別處理時,使用類模板的特化。
類模板特化的幾種類型:一是特化為絕對類型;二是特化為引用,指針類型;三是特化為另外一個類模板。
示例:
// 1、 特化為絕對類型
template<class T>
class Compare
{
public:
static bool IsEqual(const T& lh, const T& rh)
{
std::cout <<"in the general class..." <<std::endl;
return lh == rh;
}
};
// 特化為 float類型
template<>
class Compare<float>
{
public:
static bool IsEqual(const float& lh, const float& rh)
{
std::cout <<"in the float special class..." <<std::endl;
return std::abs(lh - rh) < 10e-3;
}
};
// 2 、 特化為指針或者引用類型
// 特化為T*指針
template<class T>
class Compare<T*>
{
public:
static bool IsEqual(const T* lh, const T* rh)
{
return Compare<T>::IsEqual(*lh, *rh);
}
};
// 除了T*, 我們也可以將T特化為 const T*, T&, const T&。
// 3、 特化為另外一個類模板
// 特化 任何其他的模版
template <class T1>
struct SpecializedType
{
T1 x1;
T1 x2;
};
template <class T>
class Compare<SpecializedType<T> >
{
public:
static bool IsEqual(const SpecializedType<T>& lh, const SpecializedType<T>& rh)
{
return Compare<T>::IsEqual(lh.x1 + lh.x2, rh.x1 + rh.x2);
}
};
模版總結基本這么多!!!