三、非類型模板參數(Nontype Template Parameters)

對於函數模板和類模板,模板參數不一定必須是類型,也可是是常規的數值。當以類型(type)作爲模板參數的時候,代碼中未決定的是類型;當以一般的數字(non-type)作爲模板參數的時候,代碼中待定的內容便是某些數值。使用者這種模板必須要顯示指定數值,模板才能實例化。

1、非類型類模板參數(Nontype Class Template Parameters)

之前章節中的列子中Stack類中使用vector或deque來存儲元素。我們也可以使用一個固定大小的數值來存儲元素。這麼做的好處是在定義一個Stack對象的時候就分配了固定大小的內存空間,之後的元素操作就不再需要內存分配管理了。壞處是這個數值固定大小的設置比較困難,如果設置太小很數組容易滿;如果設置太大又浪費內存空間。
一個可行方法是讓使用者自己定義數組的最大空間。
如下:

// basics/stack4.hpp 

#include <stdexcept> 

template <typename T, int MAXSIZE> 
class Stack { 
  private: 
    T elems[MAXSIZE];        // elements 
    int numElems;            // current number of elements 
  public: 
    Stack();                 // constructor 
    void push(T const&);     // push element 
    void pop();              // pop element 
    T top() const;           // return top element 
    bool empty() const {     // return whether the stack is empty 
        return numElems == 0; 
    } 
    bool full() const {      // return whether the stack is full 
        return numElems == MAXSIZE; 
    } 
}; 

// constructor 
template <typename T, int MAXSIZE> 
Stack<T,MAXSIZE>::Stack () 
  : numElems(0)              // start with no elements 
{ 
    // nothing else to do 
}

template <typename 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;  // append element 
    ++numElems;              // increment number of elements 
} 

template<typename T, int MAXSIZE> 
void Stack<T,MAXSIZE>::pop () 
{ 
    if (numElems <= 0) { 
        throw std::out_of_range("Stack<>::pop(): empty stack"); 
    } 
    --numElems;              // decrement number of elements 
} 

template <typename 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];  // return last element 
} 

第二個新的模板參數MAXSIZE是int類型,它來指定stack對象所能容納的最大元素個數。

使用這個Statck類模板的時候,需要指定元素類型和所能容納的最大元素個數。如下:

Stack<int,20>         int20Stack;    // stack of up to 20 ints 
Stack<int,40>         int40Stack;    // stack of up to 40 ints 
Stack<std::string,40> stringStack;   // stack of up to 40 strings 
// manipulate stack of up to 20 ints 
int20Stack.push(7); 
int20Stack.pop(); 

// manipulate stack of up to 40 strings 
stringStack.push("hello"); 
std::cout << stringStack.top() << std::endl; 
stringStack.pop(); 

每一個模板的實例都有自己的類型。上述的例子中,“int20Stack”和“int40Stack”是兩個不同的類型,因此這兩個類型之間是不能相互隱式或顯示類型轉換,也不能相互替換,也不能相互賦值。
可以對模板參數設置默認值,如下:

template <typename T = int, int MAXSIZE = 100> 
class Stack { 
  … 
};

2、非類型函數模板參數(Nontype Function Template Parameters)

可以爲函數模板定義非類型參數,如下:

template <typename T, int VAL> 
T addValue (T const& x) 
{ 
    return x + VAL; 
} 

3、非類型模板參數的侷限(Restrictions for Nontype Template Parameters)

非類型模板有它的侷限。通常它們只能是常數整數(constant integral values )包括枚舉,或者是指向外部鏈接的指針。

float或者類類型的對象是不被允許的,如下:

template <double VAT>        // ERROR: floating-point values are not 
double process (double v)    //        allowed as template parameters 
{ 
    return v * VAT; 
} 

template <std::string name>  // ERROR: class-type objects are not 
class MyClass {              //        allowed as template parameters 
  … 
}; 

字符串常量不能作爲模板參數,如下:

template <char const* name> 
class MyClass { 
  … 
}; 

MyClass<"hello"> x;   // ERROR: string literal "hello" not allowed 

全局指針也不能作爲非類型的模板參數,如下:

template <char const* name> 
class MyClass { 
  … 
}; 

char const* s = "hello"; 

MyClass<s> x;         // ERROR: s is pointer to object with internal linkage 

但是如下代碼是可以的:

template <char const* name> 
class MyClass { 
  … 
}; 

extern char const s[] = "hello"; 

MyClass<s> x;        // OK 

因爲全局的char類型的數組已經被初始化爲了”hello”,這是一個外部鏈接的對象。

4、總結

1、模板參數不僅僅可以是類型(type),還可以是值(value)
2、不能把float,class-type類型的對象,內部鏈接(internal linkage )對象,作爲非類型模板參數。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章