非类型模板参数
模板的参数类型有2种:类型参数和非类型参数,类型参数一般用class或typename关键字修饰;非类型参数就是用一个常量作为模板的参数,在模板中可以直接使用该参数,需要注意的是,浮点数、类对象和字符串常量是不允许作为模板的非类型参数的,同时该参数必须在编译期间就可以确认结果。
template<class T,size_t i = 10>
class Test
{
//...
};
类模板的特化
通过使用模板,我们可以实现一些与类型无关的代码,但对于一些特殊的类型,我们可能需要进行特殊处理,这就需要用到模板的特化。
template<class T>
bool Compare(const T data1, const T data2)
{
return data1<data2;
}
int main()
{
int data1=1;
int data2=2;
int* p1=&data1;
int* p2=&data2;
Compare(data1,data2);//正常使用
Compare(p1, p2);//我们希望比较指针指向的值的大小,而不是指针的大小
}
模板特化就是在原模版的基础上,对特殊类型进行特殊化的实现方式,分为函数模板特化和类模板特化。
一.函数模板特化:
//原模版
template<class T>
bool Compare(const T data1, const T data2)
{
return data1<data2;
}
//函数模板特化
//必须要有一个原模版
template<>
bool Compare<int*>(const int* data1, const int* data2)
{
return data1<data2;
}
由于使用函数模板特化函数类型容易匹配不上,因此我们不建议使用函数模板特化,而是直接将该函数给出,这种实现方式简单明了,且可读性也高。
bool Compare(const int* data1, const int* data2)
{
return data1<data2;
}
当参数类型匹配时,编译器优先使用我们给出的函数,不会去走函数模板特化。
二.类模板特化:
1.全特化
将模板参数列表的所有参数确定化就是全特化
template<class T1, class T2>
class Test
{
//...
};
//全特化
template<>
class Test<int, char>
{
//...
};
2.偏特化(半特化)
偏特化有两种表现方式:
①部分特化,即将类模板参数列表的部分参数进行特化:
template<class T1>
class Test<T1, char>
{
//...
};
②还有一种偏特化是对参数做更进一步的限制,比如参数类型是指针或者引用
template<class T1, class T2>
class Test<T1*, T2*>
{
//...
};
模板的分离编译
一个工程可能会有许多源文件,我们一般都会将声明和定义进行分离,把声明放在头文件中,定义则放在源文件里面,然后将每个源文件单独编译生成目标文件(头文件不参与编译),最后将目标文件进行链接就形成可执行程序。但如果我们将模板的声明和定义进行分离,编译器在链接时会出错。
在test.cpp中,只有模板函数的定义,没有模板函数的实例化,因此编译器不会生成具体的函数,也就不存在该模板函数实例化后的函数地址,main.cpp调用fun(int.int)函数,但在链接期间找不到函数地址,因此出错。
对于以上问题,我们有两种解决方案:
1.在模板定义位置进行显示实例化
这种方法简单粗暴,但不实用,我们不推荐使用
2.将声明和定义放到同一个头文件中