个人理解这个东西说白了就是当模板类(或函数)的类型参数为某特定值时用对应的特化定义代之。
看个例子吧
#include <iostream>
using namespace std;
template<typename T>
struct is_void
{
static const bool value = false;
};
/* 上面的代码定义了一个简单的模板结构is_void的主版本,无论类型参数T是何值,
* 结构体的静态常量成员value的值都是false,这当然是无意义的,我们需要当且
* 仅当类型参数T为void时,value成员的值为true,于是我们定义下面的特化版本
* /
template<>
struct is_void<void>
{
static const bool value = true;
};
/* 这里定义了一个is_void结构体的特化版本,将类型参数T固定为void,此时value
* 成员的值被设定为true
* /
int main(int argc, char* argv[])
{
cout << is_void<void>::value << endl;
cout << is_void<int>::value << endl;
cout << is_void<bool>::value << endl;
return 0;
}
这段代码输出为:
1
0
0
以上代码中,当is_void结构体的模板类型参数T被指定时,编译器将检查该结构体的主版本及所有特化版本,如果类型参数与某一特化版本匹配,则该特化版本将被调用,如果没有匹配的特化版本,则调用主版本。也就是说如果T的实参为void,与我们定义的特化版本的参数一至,则此特化版本的定义被应用,如果T不是void则按主版本的定义展开。
上面的例子属于全特化(full-specialisation),也就是所有的类型参数都被确定为实际类型,但有时我们可以需要对参数进行部分而不是完全的限定,这便是偏特化(partial template-class specialisation)。
看下面的例子
template<typename T>
struct is_pointer
{
static const bool value = false;
};
template<typename T>
struct is_pointer<T*>
{
static const bool value = true;
};
这个例子中我们试图判断模板参数T是否是指针类型,如果是则value成员设为true,如果不是则置为false。然而无论T是不是指针都不可能被一一列举,也就是我们不能像第一个例子一样对诸如void*, int*, long*, char* .... 等等等等一一特化,这工作量太吓人了,我要需要一种方式来将指针的情况一次性特化,这就是例子中应用的方式:偏特化。偏特化的写法我想不难理解,但注意两个版本中的T是不一样的,我们可以把特化版本的T换成T2是没有问题的,这时对于is_pointer<int*>来讲,T代表int*,而T2代表的只是int,大家可以揣摩一下。这也就间接说明了主版本和特化版本允许不同个数的模板参数的原因。
再来一个怪异一点的例子
template <typename T>
struct remove_bounds
{
typedef T type;
};
template <typename T, size_t N>
struct remove_bounds<T[N]>
{
typedef T type;
};
remove_bounds<int>::type a;
remove_bounds<int[5]>::type b;
remove_bounds<int[5][6]>::type c;
remove_bounds也就是将一个数组类型的[n]去掉即得到其基础类型,如果是非数组类型则返回原类型。于是上面的例子中,a是个int类型的变量,而b也是int类型,c有点怪,它不是int[5]类型,而是int[6]类型。这个例子也说明一个问题,特化版本与主版本的类型参数不一定一样,但typename修饰的类型参数个数是一致的。