类模板参数也可以是一个非类型模板参数。例如:
template<int hi,int wid>
class Screen
{
Screen() : _height(hi),_width(wid),_cursor(0),_Screen(hi*wid,'#') { }
// ...
private:
string _Screen ;
string::size_type _cursor ;
short _height ;
short _width ;
};
typedef Screen<24,80> termScreen ;
termScreen hp2621 ;
Screen<8,24> ancientScreen ;
绑定给非类型参数的表达式必须是一个常量表达式。即,它必须在编译时刻被计算出结果。在前面的例子中,type termScreen引用到模板实例Screen<24,80>。hi的模板实参是24,而wid的实参是80.在这两种情况下,模板实参都是常量表达式。
但是,如果给出下面定义的类模板BufPtr,那么它的实例将导致编译错误,因为来自操作符new()调用结果的指针值只有到运行时刻才能被知道:
template<int *ptr>
class BufPtr
{
// ...
};
//错误:模板实参不能在编译时刻被计算出来
BufPtr<new int[24]> bp ;
类似的,非const对象的值不是一个常量表达式,它不能被用作非类型模板参数的实参。但是,名字空间域中任何对象的地址(即便该对象不是const类型)是一个常量表达式(而局部对象的地址则不是)。因此,名字空间的对象的地址可以被用作非类型模板参数的实参。类似的,sizeof表达式的结果是一个常量表达式,所以它可以被用作非类型模板参数的实参。
template<int size> class Buf{ ... } ;
template<int *ptr> class Buf{ ... };
int size_val = 1024 ;
const int c_size_val = 1024 ;
Buf<1024> buf0 ; //正确,1024是文字常量
Buf<c_size_val> buf1 ; //正确,c_size_val是常量
Buf<sizeof(size_val)> buf2 ; //正确,sizeof(size_val)是常量,即sizeof(int)
Buf<&size_val> bp0 ; //正确,&size_val是常量,即使size_val不是常量
Buf<size_val> buf3 ; //错误,size_val不是常量,不能在编译时刻被计算出来
在模板实参的类型和非类型模板实参的类型之间允许一些转换,能被允许的转换集是“函数实参上被允许的转换”的子集。
1、左值转换,包括从左值到右值的转换,从数组到指针的转换,以及从函数到指针的转换。例如:
template<int *ptr> class BufPtr { ... }
int array[10] ;
BufPtr<array> bpObj ; //数组到指针的转换
2、限定修饰转换。例如:
template<const int *ptr> class Ptr { ... }
int iObj ;
Ptr<&iObj> pObj ; //从int *到const int*的转换
3、提升。例如:
template<int hi,int wid> class Screen { ... };
const short shi = 40 ;
const short swi = 132 ;
Screen<shi,swi> bpObj ; //从short到int的提升时
4、整值转换,例如:
template<unsigned int size> Buf { ... };
Buf<1024> bObj ; //从int到unsigned int的转换
上面描述了几种可以在模板实参的类型和非类型模板参数之间的转换。要特别注意的一点是:整型0转换成指针值的转换是不允许的。例如:
template<int *ptr>
class BufPtr { ... };
//错误:0的类型是int
//不能通过“隐式转换”隐式的转换到空指针
BufPtr<0> nil ;