模板函數:
template <typename T>
bool isEqual( const T& t1, const T& t2 )
{ return t1 == t2; // 注意:此處實際上規定了泛型T必須支持 operator== }
模板類:
template <class T> class TList{ public: ... // 編寫泛型鏈表,鏈表中儲存泛型數據 // 成員函數可能對泛型做出了一些要求,例如擁有成員函數:bool isEqual( const T& t ) bool equal( const TList<T>& t ) { return _t.isEqual( t.get() ); // 假設TList<T>::get() 返回 TList<T>類中私有成員區的_t } // Best Practices:模板程序應該盡量減少對實參類型的要求! private: T _t; ... }; class TPerson { public: ... // 編寫待存儲在TList中的類 // 提供模板中所要求的成員函數等 bool isEqual( const TPerson& t ) { return t == *this; } private: ... }; TList<TPerson> pList; // 實例化一個TPserson類對象的TList
什么是模板實例化?
通常,將從模板生成一個新類(或函數)的過程(或者,提供模板實參創建一個新模板類或函數的過程),稱為模板實例化( template instantiation )。
調用函數與實例化模板的區別?
帶實參的函數調用實際上是在程序運行時期完成的(不運行編譯后的程序當然不會產生調用函數動作啦233);
實例化模板的過程:編譯器在實例化一個模板時,首先檢查該泛型是否是一個已知類型,並確定泛型實參的個數;一旦通過這些檢查,將在代碼中生成一個新類(對上述例子來說,可能是 TList_TPerson ),以 TList 為例,編譯器重復TList的代碼(用到哪部分,就生成哪部分),用 TPerson 代替 T,然后編譯生成的類,並進一步確保生成代碼的正確性。一旦這些工作都完成,TList_TPerson 就相當於程序員定義的類。
但是,以上所有工作都在編譯時期完成,使用模板類時並沒有附帶任何運行時開銷。這就是調用函數(帶實參)與實例化模板類(帶模板實參)的主要區別。
什么時候使用操作符而不是成員函數?
仍以上述例子為例,考慮:
TList<int> pList;
以內置類型int實例化該模板。當調用 TList::equal( const TList<T>& t ) 必然產生錯誤,因為 int 類型並沒有成員函數 isEqual(const int& t)。而如果使用操作符完成上述函數:
bool equal( const TList<T>& t ) { return _t == t; }
生成的代碼將順利通過編譯。語言為所有內置類型都提供了 ==、!= 等操作符,因此,在涉及這類操作時,最好使用操作符。當然,還有另一種方式使上述功能通過編譯:模板特例化。
需要注意的是,若使用操作符,泛型 T 必須支持操作符。
什么是顯式模板特例化?
針對模板類的特定實例,實現特定的代碼,稱為顯示模板實例化。
如何進行顯式模板特例化?
// C++ Primer Bset Practices:函數模板和類模板成員函數的定義通常放在頭文件中 // 也可以分離聲明和實現,這樣的話,需要在 main.cpp 中除了包含 testcase.h 外,也包含 testcase.cpp // 文件: testcase.h #ifndef TestCase_H #define TestCase_H #include <iostream> template<class T> class TestCase { public: TestCase<T>(const T &t, std::string &name); ~TestCase<T>(); TestCase<T> &operator=(const TestCase<T> &assign); void get(); void set(T t); private: std::string _name; T _t; }; /*----------------------implement---------------------------*/ template<class T> void TestCase<T>::get() { std::cout << "this is get()\n"; } template<class T> void TestCase<T>::set(T t) { this->_t = t; } template<class T> TestCase<T> &TestCase<T>::operator=(const TestCase<T> &assign) { this->_t = assign._t; this->_name = assign._name; return *this; } template<typename T> TestCase<T>::TestCase(const T &t, std::string &name): _t(t), _name(name) { } template<typename T> TestCase<T>::~TestCase<T>() { std::cout << "this is destructor" << std::endl; } template<> void TestCase<std::string>::get() { std::cout << "this is partial specialization \n"; } #endif //TestCase_H
在另一個文件中引入該頭文件:
// // 文件: main.cpp // #include <iostream> #include "testcase.h" //也可以在其他文件中定義模板片特殊化函數 //template<> TestCase<int>::~TestCase() { // std::cout<<"這是第二種模板偏特殊化方法"<<std::endl; //} //Best Practices: 一個特定文件所需要的所有哦模板的聲明通常一起放置在文件開始位置,出現於任何使用這些模板的代碼之前。 template<> void TestCase<std::string>::get(); // 必須向編譯器說明針對函數 TestCase<std::string>::get() 的特化版本已經定義過了,不用再定義了。不然會報錯:'TestCase<std::string>::get()被多次定義' int main() { using namespace std; string name("TestCase"); string cs("construct string"); TestCase<string> TestCase(cs, name); TestCase.get(); return 0; }
typename和class有什么區別呢?
在作為泛型形參時,typename 和 class 沒有區別,例如:
template <typename T> bool isEqual( const T& t1, const T& t2 ) { return t1 == t2; } /*------------------------------------------------------------------------------------------------------*/ template <class T> bool isEqual( const T& t1, const T& t2 ) { return t1 == t2; }
考慮以下情況:
T::size_type * p;
如何確定該語句:聲明了一個T::size_type類型的指針? or 一個乘法表達式,代表着 T 的成員 size_type 與 p 相乘?
默認情況下,C++語言假定作用於運算符訪問的名字不是類型。因此,如果我們希望使用一個模板類型參數的類型成員,就必須顯式的告訴編譯器該名字是一個類型。
typename T::size_type * p;
當我們希望通知編譯器一個名字表示類型時,必須使用關鍵字 typename,而不能使用class。