对于函数模板和类模板,模板参数不一定必须是类型,也可是是常规的数值。当以类型(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
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
第二个新的模板参数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();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
每一个模板的实例都有自己的类型。上述的例子中,“int20Stack”和“int40Stack”是两个不同的类型,因此这两个类型之间是不能相互隐式或显示类型转换,也不能相互替换,也不能相互赋值。
可以对模板参数设置默认值,如下:
template <typename T = int, int MAXSIZE = 100>
class Stack {
…
};
- 1
- 2
- 3
- 4
2、非类型函数模板参数(Nontype Function Template Parameters)
可以为函数模板定义非类型参数,如下:
template <typename T, int VAL>
T addValue (T const& x)
{
return x + VAL;
}
- 1
- 2
- 3
- 4
- 5
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
…
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
字符串常量不能作为模板参数,如下:
template <char const* name>
class MyClass {
…
};
MyClass<"hello"> x; // ERROR: string literal "hello" not allowed
- 1
- 2
- 3
- 4
- 5
- 6
- 7
全局指针也不能作为非类型的模板参数,如下:
template <char const* name>
class MyClass {
…
};
char const* s = "hello";
MyClass<s> x; // ERROR: s is pointer to object with internal linkage
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
但是如下代码是可以的:
template <char const* name>
class MyClass {
…
};
extern char const s[] = "hello";
MyClass<s> x; // OK
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
因为全局的char类型的数组已经被初始化为了”hello”,这是一个外部链接的对象。
4、总结
1、模板参数不仅仅可以是类型(type),还可以是值(value)
2、不能把float,class-type类型的对象,内部链接(internal linkage )对象,作为非类型模板参数。