C++模板給這門編程語言帶來了全新的強大的能力。於是能夠進行所謂的模板元編程。因為人們發現,使用模板可以將一些邏輯讓編譯器在編譯期間內就完成,從而避免錯誤被漏到了運行時。
學好模板對理解C++開發以及理解都有非常大的好處。一些著名的C++庫如STL、boost等將模板的特性使用的淋漓盡致。
從頭學習模板,先看看如何定義一個模板。模板的聲明使用如下形式:
template<typename T> class Widget; // uses "typename"
這就表明Widget是一個模板類,在該類中,會使用到類型為T的某個類型。這個類型T由使用模板類Widget的使用者在使用時確定。當然,模板的聲明也可以使用關鍵字class代替typename進行
template<class T> class Widget; // uses "typename"
但是,《Effective C++》中建議我們盡量使用typename而不是class來聲明一個模板。因為class給人的感覺總像是T應該是一個類,或者說是用戶自定義的類型,而typename則更像是表明T可以是任意一種類型。雖然在C++編譯器看來,這兩種聲明方式沒有任何的區別,其意義是完全一樣的。
不過,typename關鍵字在模板中還有另一種用法,以下例子依然來自於《Effective C++》:
template<typename C> // print 2nd element in void print2nd(const C& container) // container; { // this is not valid C++! if (container.size() >= 2) { C::const_iterator iter(container.begin()); // get iterator to 1st element ++iter; // move iter to 2nd element int value = *iter; // copy that element to an int std::cout << value; // print the int } }
此處類型C期望是一個容器性質的類,比如STL中的那些容器,pritn2nd模板函數試圖將容器中的第二個元素打印出來,於是,在函數內定義了一個局部變量iter,一個const迭代器,來定位到容器中的第二個元素。但是這段代碼卻是不會被編譯器認可的,因為我們想當然地將C::const_iterator認為是一個類型了,按照我們對容器的印象(比如std::vector<T>::const_iterator)。然而編譯器並不知道C是個什么類型,C::const_iterator完全可能是C類型的一個成員變量。試想如果模板函數中是這樣的:
template<typename C> void print2nd(const C& container) { C::const_iterator * x; ... }
如果C::const_iterator是一個類型,則定義了一個C::const_iterator類型的指針x,而如果C::const_iterator是類型C的一個成員變量呢,如果x正好是一個全局變量呢,這個表達式就是兩個變量的乘積了。此處產生歧義的原因就在於C::iterator意義不明確,它依賴於模板參數,我們將其稱為嵌套依賴名字(nested dependent name),為了消除歧義,C++規定,對於嵌套依賴名字,統統不解釋為類型,除非顯示聲明。這就是為什么說上面那段代碼有問題,因為編譯器不認為C::const_iterator是一個類型。那么如何顯示聲明C::iterator為一個類型呢,就是使用typename關鍵字!
template<typename C> // print 2nd element in void print2nd(const C& container) // container; { // this is not valid C++! if (container.size() >= 2) { typename C::const_iterator iter(container.begin()); // get iterator to 1st element ++iter; // move iter to 2nd element int value = *iter; // copy that element to an int std::cout << value; // print the int } }
這樣就對了。