1. 程式人生 > >類模板三種類模板參數

類模板三種類模板參數

編程 arch bool 如果 數據 目的 block target 內部

類模板三種類模板參數

實際上有三種類型模板參數:類型模板參數、模板模板參數(以模板作為模板的參數)、無類型模板參數。

1、類型模板參數
類型模板參數是我們使用模板的主要目的。我們可以定義多個類型模板參數:
template<typename T,typename Container>
class Grid
{...}
同樣,也可以為類型模板參數指定默認值:
#include <iostream>
using std::vector;
template<typename T,typename Contianer=vector<T> > //註意空格
class Grid
{...}

2、模板模板參數(template template parameter)
就是將一個模板作為另一個模板的參數。
正如上面的一個例子:
Grid<int,vector<int> > myIntGrid;
註意其中int出現了兩次,必須指定Grid和vector的元素類型都是int。
如果寫成:
Grid<int,vector> myIntGrid;
因為vector本身就是一個模板,而不是一個類型,所以這就是一個模板模板參數。指定模板模板參數有點像在常規的函數中指定函數指針參數。
函數指針類型包括返回類型和函數的參數類型。在聲明模板模板參數的時候也要包括完整的模板聲明:
首先要知道作為參數的模板的原型,比如vector
template<typename E,typename Allocator=allocator<E> >
class vector
{...};
然後就可以定義:
template<typename T,template<typename E,typename Allocator=allocator<E> >class Container=vector>
class Grid
{
public:
//Omitted for brevity
Container<T>* mCells;
};
模板模板參數的一般語法:
template<other params,...,template<TemplateTypeParams> class ParameterName,other params,...>
舉例一個應用,Grid的一個構造函數:
template<typename T,template<typename E,typename Allocator=allocator<E> >class Container>
Grid<T,Container>::Grid(int inWidth,int inHeight):
mWidth(inWidth),mHeight(inHeight)
{
mCells=new Container<T> [mWidth]; //註意此處Container<T>說明,實際上還是說明 Grid<int,vector<int> >
for(int i=0;i<mWidth;++i)
mCells[i].resize(mHeight);
}
使用的時候,與一般的沒有什麽區別:
Grid<int,vector> myGrid;
myGrid.getElement(2,3);
註意:不要拘泥於它的語法實現,只要記住可以使用模板作為模板的一個參數。

3、無類型模板參數
無類型模板參數不能是對象,甚至不能是double或者float。無類型參數僅限於int、enmu、指針和引用。
有時可能想要允許用戶指定一個特定值的元素來初始化空對象,可以使用以下的方法:
template<typename T,const T EMPTY>
class Grid
{
public:
//Omitted for brevity
Grid(const Grid<T,EMPTY>& src);
Grid<T,EMPTY>& operator=( const Grid<T,EMPTY>& rhs);
//...
};
我們可以這樣使用:
Grid<int,10> myIntGrid;
Grid<int,20> myIntGrid2;
初始值可以是任意的int數,也就是必須是int、enmu、指針和引用的一種。

4、指針和引用模板參數
指針和引用模板參數必須指向所有翻譯單元中都可用的全局變量。對於這些類型的變量,相應的技術術語就是帶有外部連接的數據。
使用extern聲明即可。
如:
template<typename T ,const T& EMPTY>
class Grid
{...};
extern const int emptyInt=0;
Grid<int,emptyInt> myIntGrid;
對於初始化我們還可以使用“零初始化”即 T().

一、概念

利用模板特化機制實現編譯期條件選擇結構,利用遞歸模板實現編譯期循環結構,模板元程序則由編譯器在編譯期解釋執行。

模板是C++支持參數化多態的工具,使用模板可以使用戶為類或者函數聲明一種一般模式,使得類中的某些數據成員或者成員函數的參數、返回值取得任意類型。

  模板是一種對類型進行參數化的工具;

  通常有兩種形式:函數模板類模板

  函數模板針對僅參數類型不同的函數

  類模板針對僅數據成員成員函數類型不同的類。

  使用模板的目的就是能夠讓程序員編寫與類型無關的代碼。比如編寫了一個交換兩個整型int 類型的swap函數,這個函數就只能實現int 型,對double,字符這些類型無法實現,要實現這些類型的交換就要重新編寫另一個swap函數。使用模板的目的就是要讓這程序的實現與類型無關,比如一個swap模板函數,即可以實現int 型,又可以實現double型的交換。模板可以應用於函數和類。下面分別介紹。

  註意:模板的聲明或定義只能在全局,命名空間或類範圍內進行。即不能在局部範圍,函數內進行,比如不能在main函數中聲明或定義一個模板。

二、通式

1、函數模板通式

template <class 形參名,class 形參名,...> 返回類型 函數名(參數列表)
{
    ... //函數體
}

其中template和class是關鍵字,class可以用typename 關鍵字代替在這裏typename 和class沒區別

<>括號中的參數叫模板形參,模板形參和函數形參很相像,模板形參不能為空。一但聲明了模板函數就可以在該函數中使用內置類型的地方都可以使用模板形參名。模板形參需要調用該模板函數時提供的模板實參來初始化模板形參,一旦編譯器確定了實際的模板實參類型就稱他實例化了函數模板的一個實例。比如swap的模板函數形式為

template <class T> void swap(T& a, T& b){}

當調用這樣的模板函數時類型T就會被被調用時的類型所代替,比如swap(a,b)其中a和b是int 型,這時模板函數swap中的形參T就會被int 所代替,模板函數就變為swap(int &a, int &b)。而當swap(c,d)其中c和d是double類型時,模板函數會被替換為swap(double &a, double &b),這樣就實現了函數的實現與類型無關的代碼。

註意:

對於函數模板而言不存在 h(int,int) 這樣的調用,不能在函數調用的參數中指定模板形參的類型,對函數模板的調用應使用實參推演來進行,即只能進行 h(2,3) 這樣的調用,或者int a, b; h(a,b)。

2、類模板通式

template<class  形參名,class 形參名,…> class 類名{

// 類定義...
};

類模板和函數模板都是以template開始後接模板形參列表組成,模板形參不能為空,一但聲明了類模板就可以用類模板的形參名聲明類中的成員變量和成員函數,即可以在類中使用內置類型的地方都可以使用模板形參名來聲明。比如

template<class T> class A{public: T a; T b; T hy(T c, T &d);};

在類A中聲明了兩個類型為T的成員變量a和b,還聲明了一個返回類型為T帶兩個參數類型為T的函數hy。

對於類模板,模板形參的類型必須在類名後的尖括號中明確指定。比如A<2> m;用這種方法把模板形參設置為int是錯誤的(編譯錯誤:error C2079: ‘a‘ uses undefined class ‘A<int>‘)類模板形參不存在實參推演的問題。也就是說不能把整型值2推演為int 型傳遞給模板形參。要把類模板形參調置為int 型必須這樣指定A<int> m

在類模板外部定義成員函數的方法為:

template<模板形參列表> 函數返回類型 類名<模板形參名>::函數名(參數列表){函數體}

比如有兩個模板形參T1,T2的類A中含有一個void h()函數,則定義該函數的語法為:

template<class T1,class T2> void A<T1,T2>::h(){}

註意:當在類外面定義類的成員時template後面的模板形參應與要定義的類的模板形參一致。

再次提醒註意:模板的聲明或定義只能在全局,命名空間或類範圍內進行。即不能在局部範圍,函數內進行,比如不能在main函數中聲明或定義一個模板。

其中,template 是聲明類模板的關鍵字,表示聲明一個模板,模板參數可以是一個,也可以是多個,可以是類型參數 ,也可以是非類型參數。類型參數由關鍵字 class或typename 及其後面的標識符構成。非類型參數由一個普通參數構成,代表模板定義中的一個常量。例:

template<class type,int width>

//type為類型參數,width為非類型參數
class Graphics;

註意:

(1)如果在全局域中聲明了與模板參數同名的變量,則該變量被隱藏掉。

(2)模板參數名不能被當作類模板定義中類成員的名字。

(3)同一個模板參數名在模板參數表中只能出現一次。

(4)在不同的類模板或聲明中,模板參數名可以被重復使用。

三、優劣及適用情況

通過將計算從運行期轉移至編譯期,在結果程序啟動之前做盡可能多的工作,最終獲得速度更快的程序。也就是說模板元編程的優勢在於:

1.以編譯耗時為代價換來卓越的運行期性能(一般用於為性能要求嚴格的數值計算換取更高的性能)。通常來說,一個有意義的程序的運行次數(或服役時間)總是遠遠超過編譯次數(或編譯時間)。

2.提供編譯期類型計算,通常這才是模板元編程大放異彩的地方。

模板元編程技術並非都是優點:

1.代碼可讀性差,以類模板的方式描述算法也許有點抽象。

2.調試困難,元程序執行於編譯期,沒有用於單步跟蹤元程序執行的調試器(用於設置斷點、察看數據等)。程序員可做的只能是等待編譯過程失敗,然後人工破譯編譯器傾瀉到屏幕上的錯誤信息。

3.編譯時間長,通常帶有模板元程序的程序生成的代碼尺寸要比普通程序的大,

4.可移植性較差,對於模板元編程使用的高級模板特性,不同的編譯器的支持度不同。

四、技術細節

模板元編程使用靜態C++語言成分,編程風格類似於函數式編程,在模板元編程中,主要操作整型(包括布爾類型、字符類型、整數類型)常量和類型,不可以使用變量、賦值語句和叠代結構等。被操縱的實體也稱為元數據(Metadata),所有元數據均可作為模板參數。

由於在模板元編程中不可以使用變量,我們只能使用typedef名字和整型常量。它們分別采用一個類型和整數值進行初始化,之後不能再賦予新的類型或數值。如果需要新的類型或數值,必須引入新的typedef名字或常量。

五、其他範例

// 主模板
template<int N>
struct Fib
{
    enum { Result = Fib<N-1>::Result + Fib<N-2>::Result };
};

// 完全特化版
template <>
struct Fib<1>
{
    enum { Result = 1 };
};


// 完全特化版
template <>
struct Fib<0>
{
    enum { Result = 0 };
};

int main()
{
    int i = Fib<10>::Result;
    // std::cout << i << std::endl;
} 
// 僅聲明
struct Nil;

// 主模板
template <typename T>
struct IsPointer
{
    enum { Result = false };
    typedef Nil ValueType;
};

// 局部特化
template <typename T>
struct IsPointer<T*>
{
    enum { Result = true };
    typedef T ValueType;
};

// 示例
int main()
{
    cout << IsPointer<int*>::Result << endl;
    cout << IsPointer<int>::Result << endl;
    IsPointer<int*>::ValueType i = 1;
    //IsPointer<int>::ValueType j = 1;  
    // 錯誤:使用未定義的類型Nil
}
//主模板
template<bool>
struct StaticAssert;

// 完全特化
template<> 
struct StaticAssert<true>
{};

// 輔助宏
#define STATIC_ASSERT(exp){ StaticAssert<((exp) != 0)> StaticAssertFailed; }

int main()
{
    STATIC_ASSERT(0>1);
}

參考資料

[1] http://www.cnblogs.com/salomon/archive/2012/06/04/2534787.html

[2] http://www.cnblogs.com/assemble8086/archive/2011/10/02/2198308.html

[3] http://www.cnblogs.com/gw811/archive/2012/10/25/2738929.html

一、模板形參概述

有三種類型的模板形參:類型形參,非類型形參和模板形參。

二、類型形參

2.1 、類型模板形參

類型形參由關見字class或typename後接說明符構成,如template<class T> void h(T a){};其中T就是一個類型形參,類型形參的名字由用戶自已確定。模板形參表示的是一個未知的類型。模板類型形參可作為類型說明符用在模板中的任何地方,與內置類型說明符或類類型說明符的使用方式完全相同,即可以用於指定返回類型,變量聲明等。

2.2、 不能為同一個模板類型形參指定兩種不同的類型

比如:

template<class T>void h(T a, T b){},語句調用h(2, 3.2)將出錯,因為該語句給同一模板形參T指定了兩種類型,第一個實參2把模板形參T指定為int,而第二個實參3.2把模板形參指定為double,兩種類型的形參不一致,會出錯。(針對函數模板)

註意:上面的結論針對函數模板是正確的,但是不適用於類模板。下面將對類模板的情況進行補充。

當我們聲明類對象為:A<int> a,比如template<class T>T g(T a, T b){},語句調用a.g(2, 3.2)在編譯時不會出錯,但會有警告,因為在聲明類對象的時候已經將T轉換為int類型,而第二個實參3.2把模板形參指定為double,在運行時,會對3.2進行強制類型轉換為3。當我們聲明類的對象為:A<double> a,此時就不會有上述的警告,因為從int到double是自動類型轉換。

驗證代碼如下:

//TemplateDemo.h

#ifndef TEMPLATE_DEMO_HXX
#define TEMPLATE_DEMO_HXX

template<class T> class A{
    public:
        T g(T a,T b);
        A();
};

#endif

//TemplateDemo.cpp

#include<iostream.h>
#include "TemplateDemo.h"

template<class T> A<T>::A(){}

template<class T> T A<T>::g(T a,T b){
    return a+b;
}

void main(){
    A<int> a;
    cout<<a.g(2,3.2)<<endl;
}

編譯結果:

warning C4244: “參數”: 從“double”轉換到“int”,可能丟失數據

三、非類型形參

(1)模板的非類型形參

模板的非類型形參也就是內置類型形參,如template<class T, int a> class B{};其中int a就是非類型的模板形參。

(2)非類型形參在模板定義的內部是常量值,也就是說非類型形參在模板的內部是常量。

(3)模板的非類型形參只能是整型,指針和引用

double,String, String **這樣的類型是不允許的。但是double &,double *,對象的引用或指針是正確的。

(4)調用非類型模板形參的實參必須是一個常量表達式,即他必須能在編譯時計算出結果。

(5)註意:任何局部對象,局部變量,局部對象的地址,局部變量的地址都不是一個常量表達式,都不能用作非類型模板形參的實參。全局指針類型,全局變量,全局對象也不是一個常量表達式,不能用作非類型模板形參的實參。

(6) 全局變量的地址或引用,全局對象的地址或引用const類型變量是常量表達式,可以用作非類型模板形參的實參。

(7)sizeof表達式的結果是一個常量表達式,也能用作非類型模板形參的實參。

(8)當模板的形參是整型時調用該模板時的實參必須是整型的,且在編譯期間是常量,比如template <class T, int a> class A{};如果有int b,這時A<int, b> m;將出錯,因為b不是常量,如果const int b,這時A<int, b> m;就是正確的,因為這時b是常量。

(9)非類型形參一般不應用於函數模板中,比如有函數模板template<class T, int a> void h(T b){},若使用h(2)調用會出現無法為非類型形參a推演出參數的錯誤,對這種模板函數可以用顯示模板實參來解決,如用h<int, 3>(2)這樣就把非類型形參a設置為整數3。顯示模板實參在後面介紹。

(10) 非類型模板形參的形參和實參間所允許的轉換

a、允許從數組到指針,從函數到指針的轉換。如:template <int *a> class A{}; int b[1]; A<b> m;即數組到指針的轉換

b、const修飾符的轉換。如:template<const int *a> class A{}; int b; A<&b> m; 即從int *到const int *的轉換。

c、提升轉換。如:template<int a> class A{}; const short b=2; A<b> m; 即從short到int 的提升轉換

d、整值轉換。如:template<unsigned int a> class A{}; A<3> m; 即從int 到unsigned int的轉換。

e、常規轉換。

實例:

//由用戶自己親自指定棧的大小,並實現棧的相關操作
//TemplateDemo.h

#ifndef TEMPLATE_DEMO_HXX
#define TEMPLATE_DEMO_HXX

template<class T,int MAXSIZE> class Stack{//MAXSIZE由用戶創建對象時自行設置
    private:
        T elems[MAXSIZE];    // 包含元素的數組
        int numElems;    // 元素的當前總個數
    public:
        Stack();    //構造函數
        void push(T const&);    //壓入元素
        void pop();        //彈出元素
        T top() const;    //返回棧頂元素
        bool empty() const{     // 返回棧是否為空
            return numElems == 0;
        }
        bool full() const{    // 返回棧是否已滿
            return numElems == MAXSIZE;
        }
};

template <class T,int MAXSIZE> 
Stack<T,MAXSIZE>::Stack():numElems(0){     // 初始時棧不含元素
    // 不做任何事情
}

template <class T,int MAXSIZE>
void Stack<T, MAXSIZE>::push(T const& elem){
    if(numElems == MAXSIZE){
        throw std::out_of_range("Stack<>::push(): stack is full");
    }
    elems[numElems] = elem;   // 附加元素
    ++numElems;               // 增加元素的個數
}

template<class T,int MAXSIZE>
void Stack<T,MAXSIZE>::pop(){
    if (numElems <= 0) {
        throw std::out_of_range("Stack<>::pop(): empty stack");
    }
    --numElems;               // 減少元素的個數
}

template <class T,int MAXSIZE>
T Stack<T,MAXSIZE>::top()const{
    if (numElems <= 0) {
        throw std::out_of_range("Stack<>::top(): empty stack");
    }
    return elems[numElems-1];  // 返回最後一個元素
}

#endif

//TemplateDemo.cpp

#include<iostream.h>
#include <iostream>
#include <string>
#include <cstdlib>
#include "TemplateDemo.h"

int main(){
    try {
        Stack<int,20>  int20Stack;  // 可以存儲20個int元素的棧
        Stack<int,40>  int40Stack;  // 可以存儲40個int元素的棧
        Stack<std::string,40> stringStack; // 可存儲40個string元素的棧

        // 使用可存儲20個int元素的棧
        int20Stack.push(7);
        std::cout << int20Stack.top() << std::endl;    //7
        int20Stack.pop();

        // 使用可存儲40個string的棧
        stringStack.push("hello");
        std::cout << stringStack.top() << std::endl;    //hello
        stringStack.pop();    
        stringStack.pop();    //Exception: Stack<>::pop<>: empty stack
        return 0;
    }
    catch (std::exception const& ex) {
        std::cerr << "Exception: " << ex.what() << std::endl;
        return EXIT_FAILURE;  // 退出程序且有ERROR標記
    }
}

參考資料

[1] http://www.cnblogs.com/gw811/archive/2012/10/25/2738929.html

一、類模板的默認模板參數原則

  1、可以為類模板的類型形參提供默認值,但不能為函數模板的類型形參提供默認值。函數模板和類模板都可以為模板的非類型形參提供默認值。

  2、類模板的類型形參默認值形式為:

template<class T1, class T2=int> class A{};

為第二個模板類型形參T2提供int型的默認值。

  3、類模板類型形參默認值和函數的默認參數一樣,如果有多個類型形參則從第一個形參設定了默認值之後的所有模板形參都要設定默認值,比如

template<class T1=int, class T2>class A{};

就是錯誤的,因為T1給出了默認值,而T2沒有設定。

  4、在類模板的外部定義類中的成員時template 後的形參表應省略默認的形參類型。比如

template<class  T1, class T2=int> class A{public: void h();}; 

定義方法為

template<class T1,class T2> void A<T1,T2>::h(){}

二、驗證上述原則

//定義帶默認類型形參的類模板。這裏把T2默認設置為int型。
template<class T1,class T2=int> class CeilDemo{
public:
    int ceil(T1,T2);
};

//在類模板的外部定義類中的成員時template 後的形參表應省略默認的形參類型。
template<class T1,class T2> 
int CeilDemo<T1,T2>::ceil(T1 a,T2 b){
    return a>>b;
}

int main(){
    CeilDemo<int> cd;
    cout<<cd.ceil(8,2.5)<<endl;

    return 0;
}

輸出2(8右移2位),另外會報一個double轉int會丟失信息的warning。

在類模板的外部定義類中的成員時template 後的形參表應省略默認的形參類型,如果沒有省略,可能會依編譯器不同有不同的處理方案(之前的vc可能只是報warning),我在vs2012和g++上是報錯:

error C4519: 僅允許在類模板上使用默認模板參數

可見這裏編譯器將這裏的默認參數認為是函數模板的。

template<class T1=int,class T2=double,class T3=double> class CeilDemo{
public:
    double ceil(T1,T2,T3);
};

template<class T1,class T2,class T3> 
double CeilDemo<T1,T2,T3>::ceil(T1 a,T2 b,T3 c){
    return a+b+c;
}

void main(){
    CeilDemo<> cd;
    cout<<cd.ceil(2.5 ,3 ,4)<<endl;
}

輸出9

三、測試案例匯總

//類模板非類型形參示例
//模板的聲明或定義只能在全局,命名空間或類範圍內進行。即不能在局部範圍,函數內進行,比如不能在main函數中聲明或定義一個模板。
//類模板的定義
template<class T>class A{public:T g(T a, T b); A();};  //定義帶有一個類模板類型形參T的類A
template<class T1,class T2>class B{public:void g();}; //定義帶有兩個類模板類型形參T1,T2的類B
//定義類模板的默認類型形參,默認類型形參不適合於函數模板。
template<class T1,class T2=int> class D{public: voidg();}; //定義帶默認類型形參的類模板。這裏把T2默認設置為int型。
//template<class T1=int, class T2>class E{}; //錯誤,為T1設了默認類型形參則T1後面的所有形參都必須設置認默值。

//以下為非類型形參的定義
//非類型形參只能是整型,指針和引用,像double,String, String **這樣的類型是不允許的。但是double &,double *對象的引用或指
針是正確的。
template<class T1,int a> class Ci{public:void g();}; //定義模板的非類型形參,形參為整型
template<class T1,int &a>class Cip{public:void g();}; 
template<class T1,A<int>* m> class Cc{public:void g();}; //定義模板的模板類型形參,形參為int型的類A的對象的指針。
template<class T1,double*a>class Cd{public:void g();};  //定義模板的非類型形參,形參為double類型的引用。
class E{}; template<class T1,E &m> class Ce{}; //非類型模板形參為對象的引用。
//以下非類型形參的聲明是錯誤的。
//template<class T1,A m>class Cc{}; //錯誤,對象不能做為非類型形參,非類型模板形參的類型只能是對象的引用或指針。
//template<class T1,double a>class Cc{}; //錯誤,非類型模板的形參不能是double類型,可以是double的引用。
//template<class T1,A<int> m>class Cc{}; //錯誤,非類型模板的形參不能是對象,必須是對象的引用或指針。這條規則對於模板型參
也不例外。
//在類模板外部定義各種類成員的方法,
//typeid(變量名).name()的作用是提取變量名的類型,如int a,則cout<<typeid(a).name()將輸出int
template<class T>   A<T>::A(){cout<<"class A goucao"<<typeid(T).name()<<endl;} //在類模板外部定義類的構造函數的方法
template<class T> T A<T>::g(T a,T b){cout<<"class A g(T a,T b)"<<endl;} //在類模板外部定義類模板的成員
template<class T1,class T2>  voidB<T1,T2>::g(){cout<<"class g f()"<<typeid(T1).name()<<typeid(T2).name()<<endl;}
//在類外面定義類的成員時template後面的模板形參應與要定義的類的模板形參一致
template<class T1,int a>     voidCi<T1,a>::g(){cout<<"class Ci g()"<<typeid(T1).name()<<endl;}
template<class T1,int &a>    voidCip<T1,a>::g(){cout<<"class Cip g()"<<typeid(T1).name()<<endl;} 
//在類外部定義類的成員時,template後的模板形參應與要定義的類的模板形參一致
template<class T1,A<int> *m> voidCc<T1,m>::g(){cout<<"class Cc g()"<<typeid(T1).name()<<endl;}
template<class T1,double* a> voidCd<T1,a>::g(){cout<<"class Cd g()"<<typeid(T1).name()<<endl;}

//帶有默認類型形參的模板類,在類的外部定義成員的方法。
//在類外部定義類的成員時,template的形參表中默認值應省略
template<class T1,class T2>  voidD<T1,T2>::g(){cout<<"class D g()"<<endl;}
//template<class T1,class T2=int> void D<T1,T2>::g(){cout<<"class D k()"<<endl;} //錯誤,在類模板外部定義帶有默認類型的形
參時,在template的形參表中默認值應省略。
//定義一些全局變量。
int e=2;  doubleed=2.2; double*pe=&ed;
A<int> mw; A<int> *pec=&mw; E me;

//main函數開始
int main()
{ // template<class T>void h(){} //錯誤,模板的聲明或定義只能在全局,命名空間或類範圍內進行。即不能在局部範圍,函數內進行。
//A<2> m; //錯誤,對類模板不存在實參推演問題,類模板必須在尖括號中明確指出其類型。
//類模板調用實例
A<int> ma; //輸出"class A goucao int"創建int型的類模板A的對象ma。
B<int,int> mb; mb.g(); //輸出"class B g() int int"創建類模板B的對象mb,並把類型形參T1和T2設計為int
//非類型形參的調用
//調用非類型模板形參的實參必須是一個常量表達式,即他必須能在編譯時計算出結果。任何局部對象,局部變量,局部對象的地址,局部
變量的地址都不是一個常量表達式,都不能用作非類型模板形參的實參。全局指針類型,全局變量,全局對象也不是一個常量表達式,不能
用作非類型模板形參的實參。
//全局變量的地址或引用,全局對象的地址或引用const類型變量是常量表達式,可以用作非類型模板形參的實參。
//調用整型int型非類型形參的方法為名為Ci,聲明形式為template<class T1,int a> class Ci
Ci<int,3>//正確,數值R是一個int型常量,輸出"class Ci g() int"
const int a2=3; Ci<int,a2> mci1; mci1.g(); //正確,因為a2在這裏是const型的常量。輸出"class Ci g() int"
//Ci<int,a> mci; //錯誤,int型變量a是局部變量,不是一個常量表達式。
//Ci<int,e> mci; //錯誤,全局int型變量e也不是一個常量表達式。
//調用int&型非類型形參的方法類名為Cip,聲明形式為template<class T1,int &a>class Cip
Cip<int,e> mcip;  //正確,對全局變量的引用或地址是常量表達式。
//Cip<int,a> mcip1; //錯誤,局部變量的引用或地址不是常量表達式。
//調用double*類型的非類形形參類名為Cd,聲明形式為template<class T1,double *a>class Cd
Cd<int,&ed> mcd; //正確,全局變量的引用或地址是常量表達式。
//Cd<int,pe> mcd1; //錯誤,全局變量指針不是常量表達式。
//double dd=3.3; //錯誤,局部變量的地址不是常量表達式,不能用作非類型形參的實參
//Cd<int,&e> mcd;  //錯誤,非類型形參雖允許一些轉換,但這個轉換不能實現。

//調用模板類型形參對象A<int> *的方法類名為Cc,聲名形式為template<class T1,A<int>* m> class Cc
Cc<int,&mw> mcc; mcc.g(); //正確,全局對象的地址或者引用是常量表達式
//Cc<int,&ma> mcc;  //錯誤,局部變量的地址或引用不是常量表達式。
//Cc<int,pec> mcc2;  //錯誤,全局對象的指針不是常量表達式。

//調用非類型形參E&對象的引用的方法類名為Ce。聲明形式為template<class T1,E &m> class Ce
E me1; //Ce<int,me1> mce1; //錯誤,局部對象不是常量表達式
Ce<int,me> mce;  //正確,全局對象的指針或引用是常量表達式。
//非類型形參的轉換示例,類名為Ci
//非類型形參允許從數組到指針,從函數到指針的轉換,const修飾符的轉換,提升轉換,整值轉換,常規轉換。
const short s=3; Ci<int,s> mci4?//正確,雖然short型和int不完全匹配,但這裏可以將short型轉換為int型

參考資料

[1] http://www.cnblogs.com/gw811/archive/2012/10/25/2736224.html(註:此文有多處問題,請抱著謹慎態度查看)

類模板三種類模板參數