一、什么是模板

      模板是根據(jù)參數(shù)類型生成函數(shù)和類的機制(有時稱為“參數(shù)決定類型”)。通過使用模板,可以只設(shè)計一個類來處理多種類型的數(shù)據(jù),而不必為每一種類型分別創(chuàng)建類。
      例如,創(chuàng)建一個類型安全函數(shù)來返回兩個參數(shù)中較小的一個,如果不使用Templates,必須要編寫一系列如下的函數(shù):
// min for ints
int min( int a, int b )
    return ( a < b ) ? a : b;
// min for longs
long min( long a, long b )
    return ( a < b ) ? a : b;
// min for chars
char min( char a, char b )
    return ( a < b ) ? a : b;
//etc...使用templates,可以減少重復(fù)部分,形成一個函數(shù):
template <class T> T min( T a, T b )
    return ( a < b ) ? a : b;
模板能夠減少源代碼量并提高代碼的機動性而不會降低類型安全。

二、何時使用模板

      模板經(jīng)常被用來實現(xiàn)如下功能:
1、創(chuàng)建一個類型安全的集合類(例如,堆棧)用來處理各種類型的數(shù)據(jù)
2、為函數(shù)添加額外的類型檢查以避免獲得空指針
3、合并操作符重載組來修改類型行為(例如智能指針smart pointer)
      大多數(shù)以上應(yīng)用可以不用模板實現(xiàn);但是,模板具有以下幾個優(yōu)勢:
1、開發(fā)容易。你可以只為你的類或函數(shù)創(chuàng)建一個普通的版本代替手工創(chuàng)建特殊情況處理。
2、理解容易。模板為抽象類型信息提供了一個直截了當(dāng)?shù)姆椒ā?br>3、類型安全。模板使用的類型在編譯時是明確的,編譯器可以在發(fā)生錯誤之前進行類型檢查。

三、函數(shù)模板(function templates)

      使用函數(shù)模板,你可以指定一組基于相同代碼但是處理不同類型或類的函數(shù),例如:
template <class T> void MySwap( T& a, T& b )
{
    T c( a );
    a = b; b = c;}
這段代碼定義了一個函數(shù)家族來交換函數(shù)的參數(shù)值。從這個template你可以產(chǎn)生一系列函數(shù),不僅可以交換整型、長整型,而且可以交換用戶定義類型,如果類的構(gòu)造函數(shù)和賦值操作符被適當(dāng)?shù)囟x,MySwap函數(shù)甚至可以交換類。
      另外,函數(shù)模板可以阻止你交換不同類型的對象,因為編譯器在編譯時知道參數(shù)a和b的類型。你可以像調(diào)用一個普通函數(shù)一樣調(diào)用一個函數(shù)模板函數(shù);不需要特殊的語法。例如:
int i, j;
char k;
MySwap( i, j );     //OK
MySwap( i, k );     //Error, different types.
可以對函數(shù)模板的template參數(shù)作外部說明,例如:
template<class T> void f(T) {...}
void g(char j) {
   f<int>(j);   //generate the specialization f(int)
}
      當(dāng)template參數(shù)在外部說明時,普通固定的類型轉(zhuǎn)換會轉(zhuǎn)換函數(shù)的參數(shù)為相應(yīng)的函數(shù)模板參數(shù)。在上面的的例子中,編譯器會將(char j)轉(zhuǎn)換成整型。

四、類模板(class templates)

      可以使用類模板創(chuàng)建對一個類型進行操作的類家族。
template <class T, int i> class TempClass
{
public:
    TempClass( void );
    ~TempClass( void );
    int MemberSet( T a, int b );
private:
    T Tarray[i];
    int arraysize;
};
      在這個例子中,模板類使用了兩個參數(shù),一個類型T和一個整數(shù)i,T參數(shù)可以傳遞一個類型,包括結(jié)構(gòu)和類,i參數(shù)必須傳第一個整數(shù),因為I在編譯時是一個常數(shù),你可以使用一個標(biāo)準(zhǔn)數(shù)組聲明來定義一個長度為i的成員數(shù)組。

五、模板與宏的比較(Templates vs. Macros)

      在很多方面,模板類似預(yù)處理宏,用給定的類型代替模板的變量。然而,模板和宏有很大的區(qū)別:
宏:
#define min(i, j) (((i) < (j)) ? (i) : (j))
模板:
template<class T> T min (T i, T j) { return ((i < j) ? i : j) }
      使用宏會帶來如下問題:
1、編譯器沒有辦法檢查宏的參數(shù)的類型是否一致。宏的定義中缺少特定類型的檢查。
2、參數(shù)i和j被被調(diào)用了2次。例如,如果任一個參數(shù)有增量,增量會被加兩次。
3、因為宏被預(yù)處理程序編譯,編譯器錯誤信息會指向編譯處的宏,而不是宏定義本身。而且,在編譯階段宏會在編譯表中顯露出來。

六、模板和空指針的比較(Templates VS. Void Pointers)

      現(xiàn)在很多用空指針實現(xiàn)的函數(shù)可以用模板來實現(xiàn)。空指針經(jīng)常被用來允許函數(shù)處理未知類型的數(shù)據(jù)。當(dāng)使用空指針時,編譯器不能區(qū)分類型,所以不能處理類型檢查或類型行為如使用該類型的操作符、操作符重載或構(gòu)造和析構(gòu)。
      使用模板,你可以創(chuàng)建處理特定類型的數(shù)據(jù)的函數(shù)和類。類型在模板定義里看起來是抽象的。但是,在編譯時間編譯器為每一個指定的類型創(chuàng)建了這個函數(shù)的一個單獨版本。這使得編譯器可以使用類和函數(shù)如同他們使用的是指定的類型。模板也可以使代碼更簡潔,因為你不必為符合類型如結(jié)構(gòu)類型創(chuàng)建特殊的程序。

七、模板和集合類(Templates and Collection Classes)

      模板是實現(xiàn)集合類的一個好方法。第四版及更高版本的Microsoft Foundation Class Library使用模板實現(xiàn)了六個集合類:CArray, CMap, CList, CTypedPtrArray, CtypedPtrList和 CtypedPtrMap。
      MyStack集合類是一個簡單的堆棧的實現(xiàn)。這里有兩個模板參數(shù),T和i,指定堆棧中的元素類型和堆棧中項數(shù)的最大值。push 和 pop成員函數(shù)添加和刪除堆棧中的項,并在堆棧底部增加。
template <class T, int i> class MyStack
{
    T StackBuffer[i];
    int cItems;
public:
    void MyStack( void ) : cItems( i ) {};
    void push( const T item );
    T pop( void );
};
template <class T, int i> void MyStack< T, i >::push( const T item )
{
    if( cItems > 0 )
     StackBuffer[--cItems] = item;
    else
     throw "Stack overflow error.";
    return;
}
template <class T, int i> T MyStack< T, i >::pop( void )
{
    if( cItems < i )
     return StackBuffer[cItems++]
    else
      throw "Stack underflow error.";
}

八、模板和智能指針(Templates and Smart Pointers)

      C++允許你創(chuàng)建“智能指針”(“smart pointer”)類囊括指針和重載指針操作符來為指針操作增加新的功能。模板允許你創(chuàng)建普通包裝來囊括幾乎所有類型的指針。
      如下的代碼概括了一個簡單的計數(shù)垃圾收集者參考。模板類Ptr<T>為任何從RefCount繼承的類實現(xiàn)了一個垃圾收集指針。
#include <stdio.h>
#define TRACE printf
class RefCount
{
    int crefs;
public:
    RefCount(void) { crefs = 0; }
    ~RefCount() { TRACE("goodbye(%d)\n", crefs); }
    void upcount(void) { ++crefs; TRACE("up to %d\n", crefs);}
    void downcount(void)
    {
         if (--crefs == 0)
        {
            delete this;
        }
        else
            TRACE("downto %d\n", crefs);
   }
};
class Sample : public RefCount {
public:
    void doSomething(void) { TRACE("Did something\n");}
};
template <class T>
class Ptr
{
    T* p;
public:
    Ptr(T* p_) : p(p_) { p->upcount(); }
    ~Ptr(void) { p->downcount(); }
    operator T*(void) { return p; }
    T& operator*(void) { return *p; }
    T* operator->(void) { return p; }
    Ptr& operator=(Ptr<T> &p_)
    {
          return operator=((T *) p_);
    }
    Ptr& operator=(T* p_) 
    {
          p->downcount(); p = p_; p->upcount(); return *this;
    }
};
int main() {
    Ptr<Sample> p  = new Sample; // sample #1
    Ptr<Sample> p2 = new Sample; // sample #2
    p = p2; // #1 will have 0 crefs, so it is destroyed;
                // #2 will have 2 crefs.
    p->doSomething();
    return 0;
    // As p2 and p go out of scope, their destructors call
    // downcount. The cref variable of #2 goes to 0, so #2 is
    // destroyed
}
      類RefCount 和 Ptr<T>共同為任何一個從RefCount繼承的能夠提供整數(shù)的類的每一個實例提供了一個簡單的垃圾收集解決方案。注意使用一個參數(shù)類如Ptr<T>代替多個一般類如Ptr的主要好處在于這種形式是完全的類型安全的。前面的代碼保證Ptr<T>可以被用在幾乎任何T* 使用的地方;相反,一個普通類Ptr只能提供到void*固有的轉(zhuǎn)換。
      例如,考慮一個用來創(chuàng)建和處理文件垃圾收集的類,符號、字符串等等。根據(jù)類模板Ptr<T>,編譯器可以創(chuàng)建模板類Ptr<File>,Ptr<Symbol>, Ptr<String>等等,和它們的成員函數(shù):Ptr<File>::~Ptr(), Ptr<File>::operator File*(), Ptr<String>::~Ptr(), Ptr<String>::operator String*()等等。


PS:另外轉(zhuǎn)載csdn某大牛的解釋:
類屬性用于實現(xiàn)參數(shù)化模塊,即,給程序模塊加上類型參數(shù),使其能對不同類型的數(shù)據(jù)實施相同的操作,它是多態(tài)的一種形式。  
在C++中,類屬性主要體現(xiàn)在類屬函數(shù)和類屬類中。  
一、函數(shù)模板  
  例:排序用函數(shù)模板來實現(xiàn)。  
  template   <class   T>  
  void   sort(T   elements[],   unsigned   int   count)  
  {   //取第i個元素  
  elements   [i]    
      //比較第i個和第j個元素的大小  
  elements   [i]   <   elements   [j]    
      //交換第i個和第j個元素  
    T   temp=elements   [i];  
    elements   [i]   =   elements   [j];  
    elements   [j]   =   temp;  
  }  
  ......  
  int   a[100];  
  sort(a,100);  
  double   b[200];  
  sort(b,200);  
  A   c[300];   //類A中需重載操作符:<和=,給出拷貝構(gòu)造函數(shù)  
  sort(c,300);   
  函數(shù)模板定義了一類重載的函數(shù),使用函數(shù)模板所定義的函數(shù)(模板函數(shù))時,編譯系統(tǒng)會自動把函數(shù)模板實例化。   
  模板的參數(shù)可以有多個,用逗號分隔它們,如:  
  template   <class   T1,   class   T2>  
  void   f(T1   a,   T2   b)  
  {   ......  
  }   
  模板也可以帶普通參數(shù),它們須放在類型參數(shù)的后面,調(diào)用時需顯式實例化,如:  
  template   <class   T,   int   size>  
  void   f(T   a)  
  {   T   temp[size];  
      ......      
  }   

  void   main()  
  {   f<int,10>(1);  
  }   
  有時,需要把函數(shù)模板與函數(shù)重載結(jié)合起來用,例如:   
  template   <class   T>  
  T   max(T   a,   T   b)  
  {   return   a>b?a:b;  
  }  
  …  
  int   x,y,z;  
  double   l,m,n;  
  z   =   max(x,y);  
  l   =   max(m,n);   
  問題:max(x,m)如何處理?   
  定義一個max的重載函數(shù):   
  double   max(int   a,double   b)  
  {   return   a>b?a:b;  
  }   

二、類屬類  
類定義帶有類型參數(shù),一般用類模板實現(xiàn)。  
例:定義一個棧類,其元素類型可以變化。  
  template   <class   T>  
  class   Stack  
  {       T   buffer[100];  
      public:  
          void   push(T   x);  
  T   pop();  
  };   

  template   <class   T>  
  void   Stack   <T>::push(T   x)   {   …     }  
   
  template   <class   T>  
  T   Stack   <T>::pop()   {   …   }  
  ……  
  Stack   <int>   st1;  
  Stack   <double>   st2;   
  類模板定義了若干個類,類模板的實例化是顯式的。   
  類模板的參數(shù)可以有多個,其中包括普通參數(shù),用逗號分隔它們。并且普通參數(shù)須放在類型參數(shù)的后面。   
  例:定義不同大小的棧模板   

  template   <class   T,   int   size>  
  class   Stack  
  {       T   buffer[size];  
      public:  
          void   push(T   x);  
  T   pop();  
  };   

  template   <class   T,int   size>  
  void   Stack   <T,size>::push(T   x)   {   …     }   

  template   <class   T,   int   size>  
  T   Stack   <T,size>::pop()   {   …   }  
  ……  
  Stack   <int,100>   st1;  
  Stack   <double,200>   st2;   
  注意:
   1)類模板不能嵌套(局部類模板)。   
   2)類模板中的靜態(tài)成員僅屬于實例化后的類(模板類),不同實例之間不存在共享。   
   3)模板也是一種代碼復(fù)用機制   。
模板提供了代碼復(fù)用。在使用模板時首先要實例化,即生成一個具體的函數(shù)或類。函數(shù)模板的實例化是隱式實現(xiàn)的,即由編譯系統(tǒng)根據(jù)對具體模板函數(shù)(實例化后的函數(shù))的調(diào)用來進行相應(yīng)的實例化,而類模板的實例化是顯式進行的,在創(chuàng)建對象時由程序指定。  
一個模板有很多實例,是否實例化模板的某個實例由使用點來決定,如果未使用到一個模板的某個實例,則編譯系統(tǒng)不會生成相應(yīng)實例的代碼。  
在C++中,由于模塊是分別編譯的,如果在模塊A中要使用模塊B中定義的一個模板的某個實例,而在模塊B中未使用這個實例,則模塊A無法使用這個實例,除非在模塊A中也定義了相應(yīng)的模板。因此模板是基于源代碼復(fù)用,而不是目標(biāo)代碼復(fù)用。   
例:  
  //   file1.h  
  template   <class   T>  
  class   S  
  {     T   a;  
      public:  
        void   f();  
  };   

  //   file1.cpp  
  #include   "file1.h"  
  template   <class   T>  
  void   S<T>::f()  
  {   …  
  }  
   
  template   <class   T>  
  T   max(T   x,   T   y)  
  {   return   x>y?x:y;  
  }  
   
  void   main()  
  {   int   a,b;  
      float   m,n;  
      max(a,b);  
      max(m,n);  
      S<int>   x;  
      x.f();  
  }  
   
  //   file2.cpp  
  #include   "file1.h"  
  extern   double   max(double,double);  
   
  void   sub()  
  {   max(1.1,2.2);   //Error,no   appropriate   instance  
      S<float>   x;  
      x.f();   //Error,   corresponding   instance   has   no   appropriate   implementation  
  }


再PS:轉(zhuǎn)載cppblog上某位大牛的理解:
a. 能不用高級特性就不用高級的,其實無所謂過程式編程與OOP和范型編程的高低之分,好比是數(shù)學(xué)中的加減乘除。盡可能抑制使用template的沖動
b. 象Loki中的Policy-Based design是非常高級,以至于出現(xiàn)template<template<...> class X, ...>的code,導(dǎo)致編譯時過長,由于模板天生就是內(nèi)聯(lián)語義,一個優(yōu)化的compiler即使在開啟mix size選項時,還是有太多的展開代碼,所以編譯時長長,程序大大。還是在確實存在設(shè)計時就可預(yù)見的概念互補,可替換時使用template帶Policy-Based design。
c. 對于Template Meta Programming和Loki中使用TypeList做編譯時產(chǎn)生繼承體系的方法,個人覺得用于程序優(yōu)化很好,其實它們可以用不少現(xiàn)存的替換方案解決的,不過要學(xué)習(xí)些新東西,比如:ML,一些與c++內(nèi)嵌腳本語言,還有專門的指令優(yōu)化(MMX,SSE),還可以自制一個代碼生成器,或是一些預(yù)計算技術(shù)。
d. 一個不成熟的觀點,當(dāng)用template時想想真的需要這么做嗎?