C++ 模版
模板是C++支持參數(shù)化多態(tài)的工具方庭,使用模板可以使用戶為類或者函數(shù)聲明一種一般模式翩瓜,使得類中的某些數(shù)據(jù)成員或者成員函數(shù)的參數(shù)跨扮、返回值取得任意類型序无;模板是一種對類型進行參數(shù)化的工具。
通常有兩種形式:函數(shù)模板和類模板衡创;
函數(shù)模板:針對僅參數(shù)類型不同的函數(shù)帝嗡;
類模板: 針對僅數(shù)據(jù)成員和成員函數(shù)類型不同的類。
注意:模板的聲明或定義只能在全局璃氢,命名空間或類范圍內(nèi)進行丈探。即不能在局部范圍內(nèi)進行。
1拔莱、 函數(shù)模版
template <class 形參名碗降,class 形參名,......> 返回類型 函數(shù)名(參數(shù)列表)
{
函數(shù)體
}
1塘秦、其中template和class是關見字讼渊,class可以用typename 關見字代替,在這里typename 和class沒區(qū)別,這里說名一下兩個的區(qū)別和相同:
--- 在聲明 template parameters(模板參數(shù))時尊剔,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后面的字符串為一個類型名稱,而不是成員函數(shù)或者成員變量奶甘,這個時候如果前面沒有
typename篷店,編譯器沒有任何辦法知道T::LengthType是一個類型還是一個成員名稱(靜態(tài)數(shù)據(jù)成員或者靜態(tài)函數(shù)),所以編譯不能夠通過臭家。
2疲陕、 <>括號中的參數(shù)叫模板形參
模板形參和函數(shù)形參很相像,模板形參一般不能為空钉赁。為空的時候后面需要具解釋這個用法特化處理蹄殃。這里我們先介紹不為空的。
一但聲明了模板函數(shù)就可以用模板函數(shù)的形參名聲明類中的成員變量和成員函數(shù)你踩,即可以在該函數(shù)中使用內(nèi)置類型的地方都可以使用模板形參名诅岩。模板形參需要調(diào)用該模板函數(shù)時提供的模板實參來初始化模板形參,一旦編譯器確定了實際的模板實參類型就稱他實例化了函數(shù)模板的一個實例带膜。
注意:對于函數(shù)模板而言不存在 h(int,int) 這樣的調(diào)用吩谦,不能在函數(shù)調(diào)用的參數(shù)中指定模板形參的類型,對函數(shù)模板的調(diào)用應使用實參推演來進行钱慢,即只能進行 h(2,3) 這樣的調(diào)用逮京,或者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 類名 // 定義
{ ... };
類模板對象的創(chuàng)建:比如一個模板類A览绿,則使用類模板創(chuàng)建對象的方法為A<int> m;
對于類模板策严,模板形參的類型必須在類名后的尖括號中明確指定。比如A<2> m;用這種方法把模板形參設置為int是錯誤的饿敲。
在類模板外部定義成員函數(shù)的方法為:
template<模板形參列表> 函數(shù)返回類型 類名<模板形參名>::函數(shù)名(參數(shù)列表){函數(shù)體}妻导,
比如有兩個模板形參T1,T2的類A中含有一個void h()函數(shù)怀各,則定義該函數(shù)的語法為:
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后接說明符構(gòu)成硕并,如template<class T> void h(T a){};其中T就是一個類型形參,類型形參的名字由用戶自已確定秧荆。模板形參表示的是一個未知的類型倔毙。
注意:
對于函數(shù)模版:不能為同一個模板類型形參指定兩種不同的類型,比如template<class T>void h(T a, T b){}乙濒,語句調(diào)用h(2, 3.2)將出錯陕赃。
對于類模版:聲明類對象為:A<int> a,比如template<class T>T g(T a, T b){}琉兜,語句調(diào)用a.g(2, 3.2)在編譯時不會出錯凯正,有警告毙玻;當聲明A<double> a,警告都不會有豌蟋。
2、 非類型形參
1 桑滩、非類型模板形參:模板的非類型形參也就是內(nèi)置類型形參梧疲,如template<class T, int a> class B{};其中int a就是非類型的模板形參。
2运准、 非類型形參在模板定義的內(nèi)部是常量值幌氮。
3、 非類型模板的形參只能是整型胁澳,指針和引用该互。
4、 調(diào)用非類型模板形參的實參必須是一個常量表達式韭畸,即他必須能在編譯時計算出結(jié)果宇智。常量表達式有:
---1> 全局變量的地址或引用,全局對象的地址或引用const類型變量是常量表達式胰丁,可以用作非類型模板形參的實參随橘。
---2> sizeof表達式的結(jié)果是一個常量表達式,也能用作非類型模板形參的實參锦庸。
5机蔗、當模板的形參是整型時調(diào)用該模板時的實參必須是整型的,且在編譯期間是常量甘萧,比如template <class T, int a> class A{};如果有int b萝嘁,這時A<int, b> m;將出錯,因為b不是常量扬卷,如果const int b牙言,這時A<int, b> m;就是正確的,因為這時b是常量邀泉。
6嬉挡、非類型形參一般不應用于函數(shù)模板中钝鸽,比如有函數(shù)模板template<class T, int a> void h(T b){},若使用h(2)調(diào)用會出現(xiàn)無法為非類型形參a推演出參數(shù)的錯誤庞钢,對這種模板函數(shù)可以用顯示模板實參來解決拔恰,如用h<int, 3>(2)這樣就把非類型形參a設置為整數(shù)3。顯示模板實參在后面介紹基括。
7颜懊、 非類型模板形參的形參和實參間所允許的轉(zhuǎn)換
1、允許從數(shù)組到指針风皿,從函數(shù)到指針的轉(zhuǎn)換河爹。如:template <int *a> class A{}; int c[1]; A<c> m;即數(shù)組到指針的轉(zhuǎn)換
2、const修飾符的轉(zhuǎn)換桐款。如:template<const int *a> class A{}; int b; A<&b> m; 即從int *到const int *的轉(zhuǎn)換咸这。
3、提升轉(zhuǎn)換魔眨。如:template<int a> class A{}; const short c=2; A<c> m; 即從short到int 的提升轉(zhuǎn)換
4媳维、整值轉(zhuǎn)換。如:template<unsigned int a> class A{}; A<3> m; 即從int 到unsigned int的轉(zhuǎn)換遏暴。
5侄刽、常規(guī)轉(zhuǎn)換。
注意: 任何局部對象朋凉,局部變量州丹,局部對象的地址,局部變量的地址都不是一個常量表達式杂彭,都不能用作非類型模板形參的實參墓毒。全局指針類型,全局變量盖灸,全局對象也不是一個常量表達式蚁鳖,不能用作非類型模板形參的實參。
補充:
類模板的默認模板類型形參:
1赁炎、可以為類模板的類型形參提供默認值醉箕,但不能為函數(shù)模板的類型形參提供默認值。函數(shù)模板和類模板都可以為模板的非類型形參提供默認值徙垫。
2讥裤、類模板的類型形參默認值形式為:template<class T1, class T2=int> class A{};為第二個模板類型形參T2提供int型的默認值。
3姻报、類模板類型形參默認值和函數(shù)的默認參數(shù)一樣己英,默認應該在后面,如果第一個設置默認吴旋,那么后面其他的都應該設置默認损肛。
4厢破、 在類模板的外部定義類中的成員時template 后的形參表應省略默認的形參類型。如: template<class T1, class T2=int> class A{public: void h();}; 外部函數(shù) :template<class T1,class T2> void A<T1,T2>::h(){}治拿。
4摩泪、 模版特化
特化:為已有的模板參數(shù)進行一些使其特殊化的指定,使得以前不受任何約束的模板參數(shù)劫谅,或受到特定的修飾(例如const或者變成為了指針之類见坑,甚至是經(jīng)過別的模板類包裝之后的模板類型)或完全被指定了下來。
分類:
------- 針對特化的對象不同捏检,分為兩類:函數(shù)模板的特化和類模板的特化荞驴。
函數(shù)模板的特化:當函數(shù)模板需要對某些類型進行特化處理,稱為函數(shù)模板的特化贯城。
類模板的特化:當類模板內(nèi)需要對某些類型進行特別處理時熊楼,使用類模板的特化。
------- 特化整體上分為全特化和偏特化
全特化:全特化的類中的函數(shù)可以與模板類不一樣冤狡。
偏特化:就是模板中的模板參數(shù)沒有被全部確定孙蒙,需要編譯器在編譯時進行確定项棠。
注意: 模板函數(shù)只能全特化悲雳,沒有偏特化(以后可能有);模板類是可以全特化和偏特化的香追。
還是使用上面的示例:
// 一個模版函數(shù)
template <class T>
int compare(const T &left, const T&right)
{
std::cout <<"in template<class T>..." <<std::endl;
return (left - right);
}
// 一個特化的函數(shù)
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);
}
函數(shù)模版的特化合瓢,當函數(shù)調(diào)用發(fā)現(xiàn)有特化后的匹配函數(shù)時,會優(yōu)先調(diào)用特化的函數(shù)透典,而不再通過函數(shù)模版來進行實例化晴楔。
類模板的特化:與函數(shù)模板類似,當類模板內(nèi)需要對某些類型進行特別處理時峭咒,使用類模板的特化税弃。
類模板特化的幾種類型:一是特化為絕對類型;二是特化為引用凑队,指針類型则果;三是特化為另外一個類模板。
示例:
// 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);
}
};
模版總結(jié)基本這么多?钋唷!霍狰!