C++ 模版 學習總結


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);
    }
};

模版總結基本這么多!!!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,748評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,165評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,595評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,633評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,435評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,943評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,035評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,175評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,713評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,599評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,788評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,303評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,034評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,412評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,664評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,408評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,747評論 2 370

推薦閱讀更多精彩內容

  • C++ 模板簡介 一、模板 使用模板的目的就是能夠讓程序員編寫與類型無關的代碼。 模板是一種對類型進行參數化的工具...
    MinoyJet閱讀 2,386評論 0 12
  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy閱讀 9,531評論 1 51
  • 挫折往往就是轉折的開始,不要抱怨環境更不要抱怨自己。 世界沒權利為你負責,能對你負責只能是你自己。抱怨世界你...
    學海無涯宇少閱讀 379評論 0 0