C++模板

模板是C++中泛型編程的基礎(chǔ),一個模板就是一個創(chuàng)建類或函數(shù)的公式或者說是代碼生成器,當(dāng)使用模板類型時础爬,編譯器會生成特定的類或函數(shù)阐枣,這個過程發(fā)生在編譯時马靠。C++的模板屬于類型膨脹,是真泛型蔼两,而Java里的泛型屬于類型擦除甩鳄,是偽泛型。

模板定義

模板定義以關(guān)鍵字template開始额划,后跟以個模板參數(shù)列表妙啃,這是一個逗號分隔的一個或多個模板參數(shù)的列表,用尖括號括起來俊戳。

template<typename T,typename M>

模板參數(shù)表示在類或函數(shù)定義中用到的類型或值揖赴,在使用模板時,我們顯式或隱式指定模板實(shí)參抑胎,將其綁定到模板參數(shù)上燥滑。

模板編譯

為了生成一個實(shí)例化版本,編譯器需要掌握函數(shù)或類模板成員函數(shù)的定義圆恤,所以模板的聲明和定義應(yīng)該都放在頭文件里突倍,如果是只用于源文件內(nèi)部則可以不放在頭文件腔稀。

函數(shù)模板

當(dāng)我們調(diào)用一個函數(shù)模板時,編譯器用函數(shù)實(shí)參來為我們推斷模板實(shí)參羽历,然后為我們實(shí)例化一個特定版本的函數(shù)焊虏。下面的代碼會實(shí)例化出兩個不同版本的compare。

template<typename T>
bool compare(const T& value1, const T& value2)
{
    return value1 == value2;
}

int main()
{
    cout << compare(1, 10) << endl;
    cout << compare("11", "11") << endl;
    system("pause");
}

類模板

與函數(shù)模板不同秕磷,編譯器不能為類模板推斷模板參數(shù)類型诵闭,為了使用類模板,我們必須在模板名后的尖括號中提供額外的信息澎嚣,例如vector<int>疏尿。

template<typename T>
class Printer
{
public:
    Printer<T>(T value):value_(value) {};
    void execute() 
    {
        cout << value_ << endl;
    }
private:
    T value_;
};

 
 
int main()
{
    auto printer1 = Printer<int>(100);
    auto printer2 = Printer<double>(123.4);
    auto printer3 = Printer<string>("hello world!");
    printer1.execute();
    printer2.execute();
    printer3.execute();
    system("pause");
}

定義在類模板之外的成員函數(shù)必須以關(guān)鍵字template開始,后接類模板參數(shù)列表易桃。

template<typename T>
void Printer<T>::execute2()
{
    std::cout << value_ << std::endl;
}

成員函數(shù)只有在被用到才會實(shí)例化褥琐,這一特性使得即使類型不能完全符合類模板要求,我們?nèi)匀荒苡迷擃愋蛯?shí)例化類晤郑。下面代碼中的printer1雖然不能調(diào)用execute()敌呈,但是仍然可以實(shí)例化并調(diào)用其它成員函數(shù)。

template<typename T>
class Printer
{
public:
    Printer<T>(T value):value_(value) {};
    void execute() 
    {
        cout << value_ << endl;
    }
    void printAddress()
    {
        cout << &value_ << endl;
    }
private:
    T value_;
};

 
 
int main()
{
    auto printer1 = Printer<vector<int>>({1,2,3});
    //printer1.execute();
    printer1.printAddress();
    system("pause");
}

在類模板自己的作用域中造寝,我們可以直接使用模板名而省略尖括號磕洪。

template<typename T>
class Printer
{
public:
    Printer<T>(T value):value_(value) {};
    void execute() 
    {
        cout << value_ << endl;
    }

    Printer& getSelf() { return *this; };
    Printer<T>& getSelf2() { return *this; };
    Printer& getSelf3();
private:
    T value_;
};


//類模板外使用類模板名
template<typename T>
Printer<T>& Printer<T>::getSelf3()
{
    Printer& printer = *this;
    return printer;
}
 
int main()
{
    auto printer1 = Printer<int>(100);
    printer1.getSelf().execute();
    printer1.getSelf2().execute();
    printer1.getSelf3().execute();
    system("pause");
}

類模板的每個實(shí)例都有一個獨(dú)立的靜態(tài)成員,類似其它成員函數(shù)诫龙,一個靜態(tài)成員函數(shù)只有在使用時才會實(shí)例化析显。

template<typename T>
class Printer
{
public:
    static int testValue_;
};

template<typename T>
int Printer<T>::testValue_ = 0;
 
int main()
{
    Printer<double>::testValue_ = 100;
    Printer<int>::testValue_ = 101;
    cout << Printer<double>::testValue_ << endl;
    cout << Printer<int>::testValue_ << endl;
    system("pause");
}

非類型模板參數(shù)

除了定義類型參數(shù),還可以在模板中定義非類型參數(shù)签赃,一個非類型參數(shù)表示一個值而非一個類型谷异,我們通過特定的類型名而不是typename來指定非類型模板參數(shù)

非類型模板參數(shù)可以是一個整型姊舵,或者是一個指向?qū)ο蠡蚝瘮?shù)類型的指針或引用晰绎,非類型模板參數(shù)的模板實(shí)參必須是常量表達(dá)式,從而允許編譯器在編譯時實(shí)例化模板括丁。

template<int N,int M>
int compare(const char (&value1)[N], const char(&value2)[M])
{
    return strcmp(value1,value2);
}

 
int main()
{
    cout << compare("11", "112") << endl;
    system("pause");
}

默認(rèn)模板實(shí)參

就像我們能為函數(shù)參數(shù)提供默認(rèn)參數(shù)意義,我們也可以為函數(shù)和類模板提供默認(rèn)實(shí)參:

template<typename T,typename F=less<int>>
bool compare(const T & value1, const T & value2, F func = F())
{
    return func(value1, value2);
}

template<typename T=int>
class MyClass {
public:
    T value = {};
};

 
int main()
{
    //使用默認(rèn)模板實(shí)參
    compare(111, 2222);
    compare(string("111"), string("2222"), less<string>());

    //使用默認(rèn)模板實(shí)參
    MyClass<> value1;
    MyClass<double> value2;
    value2.value = 3.1416;
    cout << value1.value << endl;
    cout << value2.value << endl;
    system("pause");
}

限制模板參數(shù)

C++不像其它語言能顯式限制模板參數(shù)伶选,例如Java里的extends史飞,Swift里的T:ClassB,好處是我們可以把要使用的成員先寫好仰税,模板實(shí)例化的時候編譯器會判斷該類型是否符合條件构资,不符合則編譯失敗。

template<typename T>
size_t getSize(const T & value)
{
    return value.size();
}


class MyClass {
public:
    size_t size() const { return 3; }
};

 
int main()
{
    vector<int> value1 = { 1,2,3,4,5 };
    MyClass value2;
    int value3;
    cout <<getSize(value1)<< endl;
    cout << getSize(value2) << endl;
    cout << getSize(value3) << endl;//錯誤陨簇,int沒有size方法
    system("pause");
}

控制模板實(shí)例化

當(dāng)多個源文件使用了相同的模板吐绵,并提供相同的模板參數(shù)時,每個文件都會有該模板的一個實(shí)例,為了避免這種額外開銷己单,我們可以使用顯式實(shí)例化唉窃,當(dāng)編譯器遇到extern template時不會在本文件中生成實(shí)例化代碼,而是從定義處實(shí)例化代碼纹笼,可能有多個extern聲明但是只能有一處定義纹份。和普通實(shí)例化不同,實(shí)例化定義會初始化所有成員廷痘,即使我們沒有使用該成員蔓涧。
Example.h

template<typename T>
class MyClass {
public:
    //int getSize() { return value.size(); };
    T value = {};
};

Example.cpp

#include "Example.h"

//定義MyClass<int>
template class MyClass<int>;

main.cpp

//聲明MyClass<int>
extern template class MyClass<int>;


int main()
{
    MyClass<int> value;   
    cout << value.value << endl;
    system("pause");
}

指定顯式模板實(shí)參

函數(shù)模板一般會推斷模板參數(shù)類型,但有時候也需要指定顯式模板實(shí)參笋额,可以只指定前面的一部分實(shí)參元暴,后面的可以讓編譯器推斷。

template <typename T1,typename T2,typename T3>
T1 getResult(T2 value1, T3 value2)
{
    return value1+value2;
}


int main()
{
    cout <<getResult<int>(1,2)<< endl;
    cout << getResult<int>(1.0, 2.2) << endl;
    cout << getResult<string>(string("hello "), string("world!")) << endl;
    system("pause");
}

重載與模板

當(dāng)有多個重載模板對一個調(diào)用提供同樣好的匹配時兄猩,編譯器會選擇最特例化的版本茉盏,越通用的模板的優(yōu)先級越低。

對于一個調(diào)用厦滤,如果一個非函數(shù)模板與一個函數(shù)模板提供同樣好的匹配援岩,則選擇非模板版本。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末掏导,一起剝皮案震驚了整個濱河市享怀,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌趟咆,老刑警劉巖添瓷,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異值纱,居然都是意外死亡鳞贷,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進(jìn)店門虐唠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來搀愧,“玉大人,你說我怎么就攤上這事疆偿≡凵福” “怎么了?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵杆故,是天一觀的道長迅箩。 經(jīng)常有香客問我,道長处铛,這世上最難降的妖魔是什么饲趋? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任拐揭,我火速辦了婚禮,結(jié)果婚禮上奕塑,老公的妹妹穿的比我還像新娘堂污。我一直安慰自己,他們只是感情好爵川,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布敷鸦。 她就那樣靜靜地躺著,像睡著了一般寝贡。 火紅的嫁衣襯著肌膚如雪扒披。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天圃泡,我揣著相機(jī)與錄音碟案,去河邊找鬼。 笑死颇蜡,一個胖子當(dāng)著我的面吹牛价说,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播风秤,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼鳖目,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了缤弦?” 一聲冷哼從身側(cè)響起领迈,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎碍沐,沒想到半個月后狸捅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡累提,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年尘喝,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片斋陪。...
    茶點(diǎn)故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡朽褪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出无虚,到底是詐尸還是另有隱情鞍匾,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布骑科,位于F島的核電站,受9級特大地震影響构拳,放射性物質(zhì)發(fā)生泄漏咆爽。R本人自食惡果不足惜梁棠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望斗埂。 院中可真熱鬧符糊,春花似錦、人聲如沸呛凶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽漾稀。三九已至模闲,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間崭捍,已是汗流浹背尸折。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留殷蛇,地道東北人实夹。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像粒梦,于是被迫代替她去往敵國和親亮航。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容

  • C++ 模板簡介 一匀们、模板 使用模板的目的就是能夠讓程序員編寫與類型無關(guān)的代碼缴淋。 模板是一種對類型進(jìn)行參數(shù)化的工具...
    MinoyJet閱讀 2,379評論 0 12
  • 3. 類設(shè)計(jì)者工具 3.1 拷貝控制 五種函數(shù)拷貝構(gòu)造函數(shù)拷貝賦值運(yùn)算符移動構(gòu)造函數(shù)移動賦值運(yùn)算符析構(gòu)函數(shù)拷貝和移...
    王偵閱讀 1,814評論 0 1
  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy閱讀 9,523評論 1 51
  • 1. 為什么要使用模板? 假如設(shè)計(jì)一個求兩參數(shù)最大值的函數(shù)昼蛀,在實(shí)踐中我們可能需要定義四個函數(shù): 這些函數(shù)幾乎相同宴猾,...
    山的那邊是什么_閱讀 11,626評論 1 7
  • 01. 2014年仇哆,從墨爾本回國的夜航中,林小瑋縮在毛毯里睡不著夫植。 多少年了讹剔,她對于任何選擇都主意堅(jiān)定,這次卻像叢...
    微書舍閱讀 613評論 1 4