對於函數模板和類模板,模板參數不一定必須是類型,也可是是常規的數值。當以類型(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 )對象,作爲非類型模板參數。