泛型編程就是以獨立於任何特定類型的方式編寫代碼,而模板是C++泛型編程的基礎.
所謂template,是針對“一個或多個尚未明確的類型”所編寫的函數或類.
使用template時,可以顯示的或隱示的將類型當作參數來傳遞.
下面是一個典型的例子,傳回兩數中的較大者:
template<class T> inline const T& MAX(const T& a,const T& b) { return a>b?a:b; }
在這里,第一行將T定義為任意數據類型,於函數被調用時由調用者指定.
這個類型有關鍵字class引導,也可用typename引導,typename其實比class更直觀.
(需要注意的是,如果用到了嵌套依賴類型,則必須要用到typename).
理解:
第一行template<class T>告訴編譯器:我在這兒定義了一個可變類型T,調用者使用什么類型你就怎么編譯吧!
缺省模板參數
template class可以有缺省參數,例如一下聲明,允許你使用一個或多個template來聲明MyClass對象:
template<class T,class container=vector<T> > class MyClass { public: MyClass(){} ~MyClass(){} protected: private: };
如果只傳遞一個參數,那么缺省參數可作為第二參數使用:
MyClass<int> x1; // equivalent to: MyClass<int,vector<int> > x2;
注意:template缺省參數根據前一個(或前一些)參數而定義。這也就意味着如果參數傳遞列表中某個參數是缺省參數,那么后面的所有參數都應該是缺省參數.
關鍵字typename
關鍵字typename被用來做為類型之前的標識符號。考慮下面例子:
template<class SubType> struct BaseType { SubType a; }; template <class T> class MyClass1 { typename T::SubType *ptr; // ... };
這里,typename指出SubType是class T中定義的一個類型,因此ptr是一個指向T::SubType的指針.
如果沒有typename,SubType將會被當成一個static成員,於是:
T::SubType * ptr;
會被解釋為類型T中的兩個子成員SubType和ptr的乘積.
成員模板
class成員函數可以是個template,但是這樣的成員template類型既不能是virtual,也不能有缺省參數,例如:
class MyClass { //... template<class T> void f(T); };
在這里,MyClass::f聲明了一個成員函數,適用於任何類型參數.
這個特性常用來為template class中的成員提供自動類型轉換,例如下面的例子中,assign()的參數x,其類型必須和調用端所提供的對象的類型完全吻合:
template<class T> class MyClass { public: MyClass(); ~MyClass(); void assign(const MyClass<T>& x) // x must have same type as *this { value=x.value; } // ... protected: private: T value; };
如果使用了兩個類型,即使兩個類型之間可以自動轉換,也會出錯:
void fun() { MyClass<double> d; MyClass<int> i; d.assign(d); // OK d.assign(i); // ERROR: i is MyClass<int> but MyClass<double> is required }
理解:
對於一個template class中的template成員,遵循“先入為主”,如果第一次指定了類型,那么后面都要和第一次保持一致.
但如果要指定兩個不同類型的類成員變量怎么辦呢?
方法很簡單,我們將成員變量在使用一個和class不同的template類型就行:
template<class T> class MyClass { public: MyClass(); ~MyClass(); template<class X> // member template void assign(const MyClass<X>& x) // allow different template types { value = x.getValu(); } T getValue() const { return value; } // ... protected: private: T value; }; void fun() { MyClass<double> d; MyClass<int> i; d.assign(d); // OK i.assign(i); // OK (int is assigned to double) }