C++ 模板浅谈
引言與概述
C++模板機制允許在定義類,函數,類型別名的時候將類型或值當作參數,這樣定義的類和函數在運行時間和空間效率上并不遜色于手工打造的非通用的代碼。
模板提供的代碼是類型安全的。
模板是一種編譯時期的機制,與手工編寫的代碼相比,并不會產生任何運行期開銷。
對模板來說,只有當一個成員函數被使用時才會被生成代碼。
一個通用的組件應該從一個或多個具體實例泛化而來,而不是簡單的從第一原理直接而來。 即我們可以先寫一個具體的函數或者類,然后改成泛化類型。
模板簡介
一個簡單的模板
template <typename C> class String { public:String();explicit String(const C* c);//..... private:static const int short_max = 15;//用于短字符串的優化int sz;C* ptr; };一個模板類的聲明與普通class的聲明差別不大,只需要添加關鍵字和將需要泛化的類型進行替換。
模板成員函數也可以不用定義在類內,也可以在外部定義,但模板成員函數本身還是一個模板,所以要顯式聲明一個模板:
template<typename C> String<C>::String(): sz(0),ptr(nullptr) {}模板實例化
模板實例化:從一個模板和一個模板實參列表生成一個類或者一個函數的過程。
{String<char> str; }即可以進行變量的聲明,注意這里我們只創建了對象,即這個類型String< char > 編譯器會為其生成默認構造函數和析構函數,不使用的成員對象或者函數則不會生成。
模板提供了一種用少量代碼來生成大量代碼的機制,但我們要小心實例代碼的泛濫,造成內存的大量占用。
通過組合模板和簡單內聯可以消除很多直接或者間接函數調用的開銷。
模板參數
模板機制的最大弱點是無法直接表達對模板參數的要求。
template <Container Cont, typename Elem>requires Equal_comparable<Cont::value_type, Elem>() int find_index(Cont& c, Elem e);但我們可以這寫來進行模板參數的檢查。
在C++20 出現了concept這種技術,可以幫助我們進行對模板參數要求的檢查。
但這種檢查是在編譯過程中非常晚的時刻進行的,而且是在抽象層次很低的層次上運行的,幫助有限。
成員類型別名
如果我們想要在類外使用模板參數,目前只有使用成員類型別名這種方法:
template <typename C> class String { public:using value_type = T; };static成員
一個static的成員只有被真正使用時才被定義。
template <typename T> struct X {static int a;static int b; }; int* p = &X<int>::a;如果這就是全部代碼,那么 a 會被報錯為無定意,而b就不會。
模板與virtual
模板成員函數不能是虛函數,如果使用那么為虛函數實現的傳統技巧虛表就很難使用,并且鏈接器的復雜性也會很高。
模板與嵌入類型
在模板中盡量避免嵌入類型,除非它們真正依賴所有的模板參數。
模板與友元
template <typename T> class B;template <typename T> void A(const B<T>& b);template <typename T> class B { public:friend void A<>(const B<T>& b); };友元后面的<>是必須的,它指明了友元是一個模板函數,如果沒有<>,則友元函數被假定為非模板函數。友元函數只有被使用時才會被實例化。
友元的設計目的是為了表達一小群緊密相關的概念,如果友元關系很復雜,那么一定是一個設計錯誤。
源碼組織
使用模板組織源碼有三種很明顯的方法:
- 在一個編譯單元中,在使用模板前包含其定義;
- 在一個編譯單元中,在使用模板前(只)包含其聲明。在模板稍后的位置(或者使用之后)包含模板定義;
- 在一個編譯單元中,在使用模板前(只)包含其聲明。在其他編譯單元中包含其定義。
很遺憾的是C++并不支持第三種實現方式。
如果一個類模板的布局或者是一個內聯函數模板的定義發生了改變,那么使用該類或者該函數的代碼都要重新編譯。
建議
沒事的,不論未來如何,太陽一定都會升起!沒事的,不論未來如何,太陽一定都會升起!沒事的,不論未來如何,太陽一定都會升起!
總結
- 上一篇: umeng推送, 生产环境deviceT
- 下一篇: java直接量(literal)