一.非类型模板参数
模板的参数可以用于传入类型,同时也可以使用常量作为参数
示例:
//N是一个整型
template<typename T, int N>
T* MallocN()
{
return new T[N];
}
int main()
{
char* p = MallocN<char,10>();
return 0;
}
- 模板的参数只能是:类型(typename/class),或整型
- 非类型的模板参必须显示实例化/缺省值
二. 模板的特化
通常我们使用类模板是因为它可以交由编译器来为我们生成由于类型不同但是功能相同的类
但是当对于某些类型,我们需要特殊处理(实现不同的功能)时,可以进行特化
1.函数模板的特化
- 必须先有基础的函数模板
- 特化的语法:template<> 函数名<特化类型> ()
- 参数列表需要和模板函数保持一致
示例:
template<typename T>
bool Equal(T lhs, T rhs)
{
return lhs == rhs;
}
//对int*类型进行特化
template<>
bool Equal<int*>(int* lhs, int* rhs)
{
return *lhs == *rhs;
}
第二个函数,为函数模板的特化,当使用的参数为
int*
时,则会调用该函数,是对指针指向内容的比较。
但是对于需要特别处理的函数,通常直接显示定义出该函数。使用函数重载来调用。
如:
bool Equal(int* lhs, int* rhs)
{
return *lhs == *rhs;
}
bool Equal(int** lhs, int** rhs)
{
return **lhs == **rhs;
}
在调动时会优先调用非模板函数而不会从该模板产生出一个实例。
2.类模板的特化
类模板的特化相比于函数模板的特化要复杂的多,通常用来特化解决某一(或某些)需要特殊处理的类型。
不能单独定义一个和类模板名相同的类,像上面函数那样特殊解决。(对于函数,编译器可以根据参数类型来判断调用,同名的类则需要其模板参数类型来判断)。
template<typename T>
class A
{};
A<int> ai;
A<char> ac;
A:是类模板、A< int >和A< char >:在是真正的类
类模板特化语法:
template<>
class 类名<特化类型>
{}
- 必须先有基础的类模板
- 特化的模板参数个数和类模板保持一致
a.全特化
全特化,即将模板参数列表中所有参数都确定化处理
template<class T1,class T2>
class A
{};
template<>
class A<int, int>
{};
template<>
class A<int, char>
{};
类A<int, int>与A<int, char>都是根据类模板A全特化出来的,可以在其类体中实现特殊的功能。
//没特化,编译器根据类模板A自动生成对象
A<char, char> acc;
//有特化处理的,会根据其特化的类生成对象
A<int, int> aii;
A<int, char> aic;
b.偏特化
偏特化,即对模板参数列表中的参数进行限制处理
- 部分参数确定
template<class T1,class T2>
class A
{};
//特化处理
template<class T1>
class A<T1, int>
{};
template<class T2>
class A<int, T2>
{};
使用时,如果能够匹配到特化处理的类模板,则会优先使用特化的
//使用类模板<T1,T2>
A<char, char> acc;
//使用第1个特化的模板,<T1,int>
A<char, int> aci;
//使用第2个特化的模板,<int,T2>
A<int, double> aid;
模板参数名(T1、T2),可以随意定义,无需和基础类模板的保存一致
- 对参数做限制
template<class T1,class T2>
class A
{};
//特化处理
template<class t1, class t2>
class A<t1*, t2*>
{};
template<class t1, class t2>
class A<t1&, t2&>
{};
下面2个特化处理的类模板,分别针对与当传入的模板参数为指针类型,引用类型时,使用该特化
//使用类模板<T1,T2>
A<char, char> acc;
//使用第1个特化的模板,<t1*,t2*>
A<char*, char*> acc1;
//使用第2个特化的模板,<t1&,t2&>
A<char&, char&> acc2;
对于上述2种偏特化方式,可以根据实际使用场景随意组合
编译器始终坚持用使用最匹配。
三.模板声明与定义
请保持对于模板(函数模板和类模板)的声明与定义都放在同一个文件中。
一般是放在**.h头文件中,在使用的地方#include " …"包含该头文件即可。或者直接放在使用的.cpp**源文件中。
//声明
template<typename T>
bool Less(T lhs, T rhs);
//定义
template<typename T>
bool Less(T lhs, T rhs)
{
return lhs < rhs;
}
//声明
template<class T1, class T2>
class A;
//定义
template<class T1, class T2>
class A
{};
对于函数模板,由于编译器对于不同的.cpp源文件是分离编译的,如果在某一个使用函数模板的.cpp中,只有该函数模板的声明。编译器是无法在编译阶段来帮我们根据模板的使用创建不同的函数。因此在链接的时候就会因为找不到函数的定义报错。
对于类(普通类和模板类),是不允许只有定义的。