解讀C++編程中類模板的三種特化
1.類模板顯式特化
為了進(jìn)行特化,首先需要一個(gè)通用的版本,稱主模板.主模板使用了標(biāo)準(zhǔn)庫(kù)堆算法. 堆 是一種線性化的樹(shù)形結(jié)構(gòu),將一個(gè)值壓入一個(gè)堆中, 實(shí)際上等于將該值插入到一個(gè)樹(shù)形結(jié)構(gòu)中;將一個(gè)值從堆中取出就等于移除并返回堆中最大值.但在處理字符的指針時(shí)會(huì)碰釘子.堆將按照指針的值進(jìn)行組織. 我們可以提供一個(gè)顯式特化版本解決此問(wèn)題(例1)如果希望除了一個(gè)針對(duì)const char*的Heap外,還希望提供一個(gè)針對(duì)char *的Heap;(例2)
//主模板 template <typename T> class Heap { private: std::vector<T> h_; public: void push(const T& val); T pop(); bool empty() const //const聲明在末尾表示該函數(shù)不能修改類變量 { return h_.empty(); } } template <typename T> void Heap<T>::push(const T& val) { h_.push_back(val); std::push_heap(h_.begin(),h_.end()); } template <typename T> T Head<T>::pop() { std::pop_head(h_.begin(),h_.end()); T tmp(h_.back()); h_.pop_back(); return tmp; }
例1
//顯示特化版本 /*********************************************** * 可以看到模板參數(shù)列表是空的,其實(shí)這根本不是一個(gè)模 * 板. 因?yàn)闆](méi)有指定任何模板參數(shù).所以模板的顯式特化又被 * 稱作"完全特化". * Heap<const char*> 完全特化,不會(huì)導(dǎo)致模板的實(shí)例化; * Heap<int> 特化,會(huì)導(dǎo)致模板的實(shí)例化; * 編譯器根據(jù)主模板的聲明來(lái)檢查類模板特化. ***********************************************/ template<>//注意,無(wú)任何參數(shù),當(dāng)然,它本來(lái)就不是一個(gè)模板 class Head<const char *> { private: std::vector<const char *> h_; public: void push(const char *pval); const char * pop(); bool empty() const //const聲明在末尾表示該函數(shù)不能修改類變量 { return h_.empty(); } }; //再次提醒, Head<const char *>不是一個(gè)模板 void Heap<const char*>::push(const char *pval) { h_.push_back(pval); std::push_heap(h_.begin(),h_.end()); }
例2
/*********************************************** * C++沒(méi)有要求顯式特化的接口必須和主模板的接口完全 * 匹配.如該例中,沒(méi)有定義主模板的empty函數(shù),并且自行增加 * 了size和capitalize兩個(gè)函數(shù). * 提醒:此例中不定義empty函數(shù)是不可取的,定義模板的 * 顯式特化和類的派生之間雖然不存在任何技術(shù)上的聯(lián)系,但 * 是用戶依然可以參考類的派生的優(yōu)點(diǎn),讓特化版本至少具有 * 主模板的基本能力. ***********************************************/ template<>//注意,無(wú)任何參數(shù),當(dāng)然,它本來(lái)就不是一個(gè)模板 class Head<char *> { private: std::vector<char *> h_; public: void push(char *pval); char * pop(); //注意,此處沒(méi)有提供empty函數(shù)喲!!! size_t size() const; void capitalize(); };
2.模板局部特化
模板局部特化首先要聲明的是,C++還不支持對(duì)函數(shù)模板的局部特化,所以此處我們只討論類模板的局部特化.我們依然首先需要一個(gè)主模板.(參考類模板顯式特化) 自我理解:如果針對(duì)不能的指針定義不同的完全特化,豈不是太麻煩了,有沒(méi)有更好的辦法呢?那就是局部特化了.(例1)提示: 局部特化它是一個(gè)模板.完全特化不是一樣模板.
例1
/*********************************************** * 局部特化 * 和完全特化不同,這里的Heap參數(shù)類型只是被部分的確 * 定為T*,而T是一個(gè)未指定的類型,這就是為什么說(shuō)它是局部 * 特化的原因; * 當(dāng)使用一個(gè)未經(jīng)任何修飾的指針類型來(lái)實(shí)例化Heap時(shí), * 局部特化將優(yōu)先于主模板; * 當(dāng)使用const char * 或 char *(參考類模板顯式特化)來(lái) * 實(shí)例化Heap時(shí),此時(shí)完全特化又會(huì)優(yōu)先于局部特化. * Heap<std::string> h1; 主模板 T是std::string * Heap<std::string *> h2; 局部特化 T是std:string * Heap<int **> h3; 局部特化 T是int * * Heap<char *> h4; 完全特化 T是char * * Heap<const int *> h5; 局部特化 T是const int * Heap<int (*)()> h6; 局部特化 T是int() ***********************************************/ template <typename T> class Heap<T *> //注意這里 { private: std::vector<T *>h_; public: void push(const T *val); T *pop(); bool empty() { return h_.empty(); } }; template <typename T> void Heap<T *>::push(const T *val) { //...... }
例2
/*********************************************** * 有一點(diǎn)很微妙但很有用:主模板的完全特化或局部特化 * 必須采用與主模板相同數(shù)量和類型的實(shí)參進(jìn)行實(shí)例化,但它 * 的模板的參數(shù)并不需要具有和主模板相同的形式. ***********************************************/ //定義一個(gè)模板,有三個(gè)模板參數(shù),書寫形式如下 template <typename R,typename A1,typename A2> //注意,局部特化中,模板參數(shù)也是三個(gè),但書寫形式可不一樣嘍 class Heap<R (*) (A1,A2)> { //...... }; Heap<char *(*) (int,int)> h7; //R是char *,A1和A2是int //把 char *(*) (int,int) 想象成一個(gè)"指向有兩個(gè)參數(shù)的非成員函數(shù)的指針" template <class C,typename T> class Heap<T C::*> { //...... }; Heap<std::string Name::*> h8;//T是string,C是Name
盡管為何需要對(duì)這些東西使用Heap只是一個(gè)猜測(cè),先知道有這么一用法吧!
3.類模板成員特化
雖然模板的特化和類的派生之間沒(méi)有任何關(guān)系, 但在特化模板的時(shí)候,不妨借鑒一下派生的精神.也就意味著一個(gè)完全特化或局部特化通常必須重新實(shí)現(xiàn) 主模板具備的所有能力.
例:
//主模板 template <typename T> class Heap { private: std::vector<T> h_; public: void push(const T& val); T pop(); bool empty() const //const聲明在末尾表示該函數(shù)不能修改類變量 { return h_.empty(); } } //其實(shí)我們真正需要特化的是 push 和 pop兩個(gè)函數(shù). //對(duì)比顯式特化,它是通過(guò)主模板,再寫一個(gè)模板顯式特化版本類; //而這里只是對(duì)類模板成員進(jìn)行了單獨(dú)特化. template<> void Heap<const char*>::push(const char *const &pval) { h_.push_back(pval); std::push_heap(h_.begin(),h_.end(),strLess); } template<> const char* Heap<const char*>::pop() { std:pop_heap(h_.begin(),h_end(),strLess); const char* tmp = h_.back(); h_.pop_back(); return tmp; }
注意,這些函數(shù)的接口必須和 "它們正在特化其成員" 的模板的相應(yīng)接口相匹配.如例1, 就得和主模板的接口相匹配.而如果你是自己再定義的一個(gè)顯式/局部特化版本類,就不需要匹配 一致.(見(jiàn)顯式特化和局部特化),最后指出兩點(diǎn): 首先,除了成員函數(shù)外,其實(shí)成員也可以被顯式特化,如靜態(tài)成員和成員模板.其次,顯式特化是為模板或模板成員提供定制版本的一種手段;而顯式實(shí)例化僅僅是明確地告訴編譯器去實(shí)例化一個(gè)成員.
相關(guān)文章
C語(yǔ)言實(shí)現(xiàn)成績(jī)統(tǒng)計(jì)示例
這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)成績(jī)統(tǒng)計(jì)示例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11C語(yǔ)言初學(xué)者代碼中的常見(jiàn)錯(cuò)誤與問(wèn)題
C語(yǔ)言初學(xué)者犯過(guò)的很多錯(cuò)誤都非常典型,在初學(xué)者中非常普遍,于是整理了一下,應(yīng)該對(duì)其他初學(xué)者有借鑒意義2013-11-11C++ String部分成員模擬實(shí)現(xiàn)流程詳解
我們先不直接實(shí)現(xiàn)完整版的string,先實(shí)現(xiàn)簡(jiǎn)易版的string部分成員來(lái)基本了解下它的框架,以及以后來(lái)學(xué)習(xí)深淺拷貝的問(wèn)題。這樣有循序漸進(jìn)的過(guò)程嘛2022-08-08C++中sort()函數(shù)和priority_queue容器中比較函數(shù)的區(qū)別詳析
C++中sort()和priority_queue都能自定義比較函數(shù),其中sort()自定義的比較函數(shù)比較好理解,priority_queue中自定義的比較函數(shù)的效果和sort()是相反的,這篇文章主要給大家介紹了關(guān)于C++中sort()函數(shù)和priority_queue容器中比較函數(shù)的區(qū)別的相關(guān)資料,需要的朋友可以參考下2023-03-03C語(yǔ)言雙指針多方法旋轉(zhuǎn)數(shù)組解題LeetCode
這篇文章主要為大家介紹了C語(yǔ)言雙指針使用多方法旋轉(zhuǎn)數(shù)組題解LeetCode,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-02-02源碼分析C++是如何實(shí)現(xiàn)string的
我們平時(shí)使用C++開(kāi)發(fā)過(guò)程中或多或少都會(huì)使用std::string,但您了解string具體是如何實(shí)現(xiàn)的嗎,本文小編就帶大家從源碼角度分析一下2023-04-04C語(yǔ)言rewind與fseek函數(shù)之隨機(jī)讀寫文件的用法詳解
這篇文章主要介紹了C語(yǔ)言rewind與fseek函數(shù)之隨機(jī)讀寫文件的用法詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-09-09