模板的形参:
1.类型形参:
template<class T>
void h(T a)
{//……}
- 类型形参是指:class T/ typename T中的T,类型形参的名字由用户自己定义,模板的形参(T)表示一个未知的类型。
- 模板类型形参可以作为类型说明符用在模板中的任何地方,与内置类型/类类型的使用方式完全相同,即可以用于指定返回类型,声明变量等地方
- 不能为同一个的模板类型形参指定两种不同的类型,比如:
template<class T>
void h(T a,T b)
{}
void test()
{
h(1,1.2);
}
这样的调用方式会出错,因为该语句给同一模板形参T指定了俩种不同的类型(int,double)
- 无论时模板函数,还是模板类都不能给同一个模板类型指定俩者不同类型的形参
2.非类型模板形参:
- 模板的非类型形参也就是内置类型形参:如:template<class T,int a>class B{},其中int a就是非类型模板形参
- 非类型模板形参在模板定义的内部是常量值
- 非类型模板形参只能是整型,指针,引用,不能使用double,string等
- 调用非类型模板形参的实参只能是一个常量表达式,即他必须能在编译时计算出结果
- 任何局部对象,局部变量,局部对象地址,局部变量地址都不是常量表达式,都不能用作非类型模板形参的实参。全局指针,全局变量,全局对象也不是一个常量表达式,不能用作非类型模板形参的实参
- 全局变量的地址或引用,全局对象的地址或引用,const类型变量是常量表达式,可以用作非类型模板形参的实参
- sizeof表达式的结果是一个常量表达式,也能用作非类型模板形参的实参
- 当模板的形参是整型时调用该函数的实参必须时整型,且在编译期间是常量。比如int b,AA<int ,b> a1;这时编译器就会报错,因为b是一个变量,如果修改为const int b,AA<int ,b> a1;就不会出错,因为此时b是一个常量
非类型模板形参的形参和实参间所允许的转换
- 允许从数组到指针,从函数到指针。
template<int *>
class A
{};
void Test()
{
int b[1];
A<b> a1;
}
2.const修饰符的转换
template<const int*a>
class A {};
void Test()
{
int* b;
A<&b> a1;//从int* 到const int* 的转换
}
3.提升转换(short——int)
4.整值转换(int ——unsigned int)
模板形参总结:
- 类型参数:可以给类模板的类型形参提供默认值,但不能给函数模板的类型形参提供默认值。
- 非类型参数:函数模板和类模板都可以为类模板的非类型形参提供默认值
- 给类模板的类型形参默认值的方式:
template<class T1,class T2=int> class AA{};
-
类模板的类型参数提供默认值的方式与函数缺省一样,要从右向左,不能给T1提供默认值而不给T2。
-
在类模板的外部定义类中的成员时template后的形参表应省略默认的形参类型
template <calss T1,class T2 = int> class A { public: void h(); }; viod A<T1,T2>::h() {}
模板的模板参数:
template<class T>
class Seqlist
{
private:
int _size;
int _capacity;
T* _data;
};
template<class T, template<class>calss Container = Seqlist>
calss Stack
{
public:
void Push(const T& x);
void pop();
const T& Top();
private:
Container<T> _con;
}
void Test()
{
Stack<int> s1;
Stack<int, Seqlist>s2;
}
3.模板的分离编译:
解决方法:
- 在模板头文件xxx.h里面显示实例化->模板类的定义后面添加template class SeqList<int>;
- 一般不推荐这种方法,一方面老编译器可能不支持,另一方面实例化依赖调用者
- 将声明和定义放在同一个文件“xxx.hpp”里面,添加使用这种方法