C++模板設(shè)計(jì) 包看不包會,看了不后悔

模板

  • 模板 Template代碼重用機(jī)制的重要工具
  • 泛型技術(shù):與數(shù)據(jù)類型無關(guān)的程序設(shè)計(jì)技術(shù)纳猫,是概念級的通用程序方法
  • 模板將算法設(shè)計(jì)從具體數(shù)據(jù)類型中分離虱岂,設(shè)計(jì)出獨(dú)立于數(shù)據(jù)類型的通用模板程序
  • 模板有函數(shù)模板類模板

C++模板

  • 在C中,可以使用宏定義來泛化設(shè)計(jì)函數(shù)台舱,但是不進(jìn)行類型檢查律杠,容易出錯
  • 使用template關(guān)鍵字定義模板潭流,在<>中用typename或class聲明模板類型
  • <>中的參數(shù)被稱為模板參數(shù),可以是類型參數(shù)(用typename和class聲明)和非類型參數(shù)(確定的數(shù)據(jù)類型)
  • 非類型模板參數(shù)必須是整數(shù)類型(包括枚舉)柜去、對象灰嫉、函數(shù)指針,或者是指向外部變量的指針
  • 與普通函數(shù)與類的接口實(shí)現(xiàn)分離的分文件存放方式不同嗓奢,模板類和函數(shù)必須將定義和聲明放在同一文件內(nèi)
  • 編譯模板時讼撒,編譯器必須知道模板的確切定義,模板函數(shù)和類的定義通常(對于大多數(shù)編譯器而言)保存在同一頭文件中(.hpp)
  • 編譯器有兩種模式股耽,包含和分離編譯模式根盒,其中分離模式中模板定義會有一個export參數(shù)告知編譯器該模板實(shí)例在生成時定義必須可見,但并不是所有編譯器都支持該模式
  • 大部分編譯器使用包含模式物蝙,此時編譯器一次只能處理一個cpp

函數(shù)模板

  • 函數(shù)模板由編譯器在其調(diào)用時確定模板類型的具體類型
  • 模板類型可以用來定義函數(shù)的返回值炎滞、形參、局部變量
  • 在遇到函數(shù)模板定義時诬乞,編譯器不會生成對應(yīng)代碼册赛,直到調(diào)用時進(jìn)行代碼生成
  • 編譯器只生成一次同類型的函數(shù)定義,第二次遇到相同類型直接調(diào)用
  • 每個模板類型在函數(shù)被調(diào)用時必須能夠被推導(dǎo)(根據(jù)參數(shù)類型判斷或直接顯式指定)
  • 如果不能進(jìn)行推導(dǎo)震嫉,編譯器會無法得知應(yīng)該生成的函數(shù)定義森瘪,產(chǎn)生錯誤
  • 模板定義后需緊跟函數(shù)聲明,一個模板可以有多個類型參數(shù)
  • 與一般函數(shù)參數(shù)傳遞不同责掏,模板函數(shù)在調(diào)用時不會進(jìn)行隱式類型轉(zhuǎn)換,可能引發(fā)類型不明確的錯誤
  • 如:函數(shù)有多個參數(shù)為T類型湃望,調(diào)用時參數(shù)接收了1(int)和1.0(double)换衬,無法明確模板類型
template<typename T> void fn2(T t1, T t2);
// 模板參數(shù)不進(jìn)行隱式類型轉(zhuǎn)換,類型不明確
    // fn2(1, 1.0);
    // fn2<int>(1, 1.0);            // 可能丟失精度的操作
    fn2<double>(1, 1.0);
    fn2(double(1), double(1.0));
  • 可以通過對參數(shù)進(jìn)行顯式轉(zhuǎn)換证芭,或在調(diào)用時顯式指定模板類型解決這一問題
  • 小技巧:將函數(shù)賦值為delete瞳浦,可以禁用對該函數(shù)的調(diào)用,通撤鲜浚可以用來避免隱式類型轉(zhuǎn)換
void fn(int a){...}
void fn(double a) = delete;
fn(1.0);    // 該調(diào)用將被阻止
  • 非類型模板參數(shù)只能是整數(shù)類型(包括枚舉)叫潦、對象、函數(shù)指針和指向外部變量的指針
template<typename T, int i, int* pi> void fn3(T t1);
  • 在調(diào)用函數(shù)時官硝,只能向非類型模板參數(shù)傳遞常值矗蕊,不能傳遞變量,并且非類型模板參數(shù)必須接受值(包括默認(rèn)值)
template<typename T, int i> void fn3(T t1);
// 不能向模板參數(shù)傳遞變量
    int i = 0;
    // fn3<char, i>(i);
    fn3<char, 1>(i);
  • 模板參數(shù)遵循一般的作用域規(guī)則氢架,但是不能在函數(shù)內(nèi)重用模板參數(shù)名
template<typename T, int i> void fn3(T t1)
{
    // 編輯器不會報錯傻咖,注意
    // 不能重用模板參數(shù)名
    int i = 0;
    int T = 0;
}

類模板

  • 類模板用來處理機(jī)構(gòu)和成員函數(shù)相同,但成員具體數(shù)據(jù)類型不同的類型
  • 類模板的定義必須緊跟模板定義岖研,中間不能有其他代碼
// 抽象類MyStack
template<typename T>
class MyStack
{
private:
    T value;
    T* top;
public:
    virtual void init() = 0;
    virtual void push(T v) = 0;
    virtual T pop() = 0;
};
  • 如果類模板有一個非類型模板參數(shù)卿操,必須在使用(包括實(shí)例化與成員對象、派生類負(fù)責(zé)基類初始化)時對其傳遞值,或使用默認(rèn)值
template<typename T, int MAXSIZE>
class MyStack
{
    ...
};
  • 在用類外用模板聲明成員函數(shù)時害淤,必須將模板聲明放在函數(shù)名前扇雕,類作用域限定符后
  • 等于需要再聲明一次模板參數(shù)列表,以告知編譯器成員函數(shù)所屬的類是一個模板類
  • 且模板類的聲明和定義都必須在同一個.h或.hpp中窥摄,防止實(shí)例化時編譯器出錯
template<typename T, int MAXSIZE>
void MyStack<T, MAXSIZE>::fn()
{
}
  • 類模板實(shí)例化包括模板實(shí)例化成員函數(shù)實(shí)例化
  • 類模板在實(shí)例化對象時必須為所有模板參數(shù)顯式指定類型镶奉,如使用STL的vector
// 必須為模板類指定模板參數(shù)的類型
    vector<int> iv;
  • 編譯器在實(shí)例化對象時才會處理類模板參數(shù),所做的操作等同于將代碼中所有類型參數(shù)替換成推導(dǎo)的參數(shù)溪王,將所有非類型參數(shù)替換為指定的值腮鞍,但是不會實(shí)例化模板成員函數(shù)

可變參數(shù)函數(shù)模板

  • C++11提供了一種可變參數(shù)的模板函數(shù),其參數(shù)類型個數(shù)都可不確定
  • 可變參數(shù)函數(shù)模板只能用于模板函數(shù)莹菱,對于普通函數(shù)需要引入頭文件stdarg.h或cstdarg并進(jìn)行相關(guān)操作
  • 可變參數(shù)模板函數(shù)必須有兩個模板參數(shù)移国,其中第二個需要用省略號...args書寫,調(diào)用時使用后綴形式args...
  • 可變參數(shù)模板函數(shù)實(shí)質(zhì)上是一種遞歸道伟,首先執(zhí)行函數(shù)迹缀,接收所有參數(shù)中的第一個參數(shù)
  • 然后將剩余參數(shù)...args中的第一個參數(shù)在調(diào)用函數(shù)時傳入
  • 因此,為了結(jié)束遞歸蜜徽,通常需要有一個只有一個模板參數(shù)的普通模板函數(shù)作為遞歸結(jié)束的函數(shù)
  • 這個普通的模板函數(shù)是對可變參數(shù)模板函數(shù)的重載祝懂,因此需要在可變參數(shù)模板函數(shù)之前被定義,否則無法結(jié)束遞歸
// 此函數(shù)將在...args只剩下一個參數(shù)時被調(diào)用拘鞋,參數(shù)個數(shù)1與之匹配
template<typename T>
void fn(T a)
{
    cout << a << endl;
}

// 此函數(shù)在...args的參數(shù)個數(shù)>=2時被調(diào)用
template<typename T,typename ...TR>
void fn(T a,TR ...args)     // 聲明函數(shù)原型時使用前綴
{
    cout << a << endl;
    // 必須在內(nèi)部遞歸砚蓬,否則只會使用第一個參數(shù)
    fn(args...);                // 調(diào)用時使用后綴
}

模板特化

  • 在一些情況下,部分類型可能因?yàn)橛胁煌倪\(yùn)算符或其他函數(shù)定義而導(dǎo)致模板函數(shù)中的運(yùn)行過程出現(xiàn)問題
  • 此時盆色,可以為這些特定的類型定義特殊的模板灰蛙,稱為模板特化
  • 模板特化是一種重載,在聲明特化模板時隔躲,將需要特化的模板參數(shù)從<>移除摩梧,并在使用的位置用具體類型說明
  • template<>在其中沒有模板參數(shù)時也需要保留,聲明其為模板特化
template<typename T>
void fn(T t)
{
    cout << "normal type" << endl;
}
template<>              // 移出模板參數(shù)列表
void fn(char t)     // 在原來使用模板參數(shù)的地方使用具體類型
{
    cout << "T is char" << endl;
}

int main()
{
    fn(1);          // normal type
    fn('c');        // T is char
}

模板設(shè)計(jì)常見注意點(diǎn)

  • 對于內(nèi)聯(lián)的模板函數(shù)和模板成員函數(shù)宣旱,inline和constexpr要放在模板定義之后
template<typename T> inline void fn(T t1);
template<typename T> constexpr void fn(T t1);
  • C++11后可以為模板參數(shù)指定默認(rèn)值仅父,且其右側(cè)其他模板參數(shù)都應(yīng)該有默認(rèn)值
template<typename T = double, int i = 1> void fn3(T t1);
  • 用模板定義的類的成員函數(shù)稱為成員模板,成員模板不能是虛函數(shù)浑吟,虛函數(shù)的個數(shù)需要在編譯時就確定
  • 模板函數(shù)可以被重載笙纤,重載規(guī)則與一般函數(shù)重載相同,要有不同的參數(shù)列表(包括模板參數(shù)列表)
  • 模板參數(shù)并不一定在所有類型下都可正常運(yùn)行组力,此時可以用普通函數(shù)對其重載粪糙,確保功能正常(注意聲明順序)
  • 模板參數(shù)調(diào)用有特定順序
    1. 普通函數(shù)
    2. 特化模板函數(shù)
    3. 普通模板函數(shù)
  • C++的函數(shù)在調(diào)用時會嘗試匹配最佳的函數(shù),并可能發(fā)生參數(shù)的隱式類型轉(zhuǎn)換忿项,對于模板函數(shù)蓉冈,只會發(fā)生如下兩種:
    1. 非const類型的實(shí)參傳遞給const類型的形參
    2. 數(shù)組或引用類型的實(shí)參轉(zhuǎn)換成指針類型的形參城舞,如數(shù)組名或其引用會轉(zhuǎn)換成數(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)容

  • 一、概述 二俱萍、模板定義與使用 ?1. 函數(shù)模板的定義與使用?2. 類模板的定義與使用 三镀首、typename 的特殊...
    從不中二的憂傷閱讀 1,660評論 1 1
  • 模板是C++中泛型編程的基礎(chǔ),一個模板就是一個創(chuàng)建類或函數(shù)的公式或者說是代碼生成器鼠次,當(dāng)使用模板類型時更哄,編譯器會生成...
    土豆吞噬者閱讀 683評論 0 0
  • 背景 C++ 是很強(qiáng)大成翩,有各種特性來提高代碼的可重用性,有助于減少開發(fā)的代碼量和工作量赦役。 C++ 提高代碼的可重用...
    小林coding閱讀 322評論 0 3
  • C++ 模板簡介 一麻敌、模板 使用模板的目的就是能夠讓程序員編寫與類型無關(guān)的代碼。 模板是一種對類型進(jìn)行參數(shù)化的工具...
    MinoyJet閱讀 2,379評論 0 12
  • 我們想要定義多個函數(shù)掂摔,每個函數(shù)比較一種給定類型的值术羔,可能定義多個重載函數(shù)。 這兩個函數(shù)幾乎是相同的乙漓,唯一不同就是參...
    Wangcy閱讀 344評論 0 0