在使用模版的过程中,大部分情况下,我们都会为所有可能的数据类型写一个通用的模版类。 而模版特殊化的思路就是继承默认的模版实现,为一个特殊的类型实现不同的处理方式。
让我们看个例子,向量,大多数情况下它都是由给定的数据类型的数组实现。 现在我们要实现一个bool类型的向量,为了节省内存,我们可以用一个integer的每一个bit来表示bool值,这样就可以用一个integer向量来实现bool类型向量。如何实现呢? 请看下面两个不同的向量类。
第一个类如下:
template |
如用上面的类来实现bool向量,你知道布尔类型的数据长度在大多数的系统中都是8bits,尽管我们只用其中的一个bit。 如果一个integer数据的每一个bit都可以人为控制,我们就可以考虑使用integer数组来实现bool向量。
要实现这个功能,我们需要对着类似于模版的类进行特殊说明,但是这个类中,所有模版参数的列表为空:
template <>
而且具体的数据类型紧随类名之后:class classname
template <>
class vector
{
// interface
private:
unsigned int *vector_data;
int length;
int size;
};
注意:这特殊化的向量类和通用的向量类已经是完全不同的两个类了,尽管他们有相同的vector模版类名。他们不共享任何的接口或者任何代码。
在此,我们需要再次重申为什么需要特殊化。在上述的例子中,主要的原因是提高实现的空间使用效率。但是我们可以想一想我们迟早都会用到这一功能的情况,比如,你想为某一特定类型的模版类添加额外的方法,而这些方法并不适用于其它数据类型。如你想为double型向量添加个返回每一double值的非整形部分的方法。 尽管你可能更喜欢用继承。在这里使用模板特殊化不妨也是一个办法。这样一来,所有的double模板类型实例都添加了这个方法。
上述是对某一个类型的模板类进行特殊化,如果你想特殊化某几个模板时,例如,你有一些sortedVector模板类型需要定义“>”操作符,而另外一组类型不包含任何重载运算符,但是包含一个比较函数,你可能需要特殊化你的模板来分别处理这些类。
局部模板特殊化源于和上述模板特殊化一样的动机。 可是这次,除了为一个特殊的类型实现一个类外,我们最终实现一个仍然允许其他参数的模版。让我们用一个具体的例子来说明这个功能。
让我们回到刚才sortVector的例子。 我们需要考虑排序中做比较操作的方式:使用”>”操作符如果它已经被实现,或者特殊化如果它没有被实现。但是现在,如果我们想在sortVector中保存指向对象的指针类型数据。 我们可以使用标准的 “>”比较指针的值来给他们排序。 (此例中,我们的向量是由低到高排序):
template
class sortedVector
{
public:
void insert (T val)
{
if ( length == vec_size ) // length is the number of elements
{
vec_size *= 2; // we'll just ignore overflow possibility!
vec_data = new T[vec_size];
}
++length; // we are about to add an element
// we'll start at the end, sliding elements back until we find the
// place to insert the new element
int pos;
for( pos = length; pos > 0 && val > vec_data[pos - 1]; --pos )
{
vec_data[pos] = vec_data[pos - 1];
}
vec_data[pos] = val;
}
// other functions...
private:
T *vec_data;
int length;
int size;
};
现在,你可能已经注意到,对于指向对象的指针类型的数据,直接比较指针的地址值进行排序是不合适的。 因此我们需要针对指针数据写如下代码:
for( pos = length; pos > 0 && *val > *vec_data[pos - 1]; --pos )
当然,如果直接修改模板的代码,其他的非指针型数据就不能工作。 因此我们需要局部特殊化来处理指针和非指针两种类型的数据(当然你可以考虑多极指针的情况)。
我们对局部特殊化模板的声明如下:它可以处理任何指针类型。
template
class sortedVector
{
public:
// same functions as before. Now the insert function looks like this:
insert( T *val )
{
if ( length == vec_size ) // length is the number of elements
{
vec_size *= 2; // we'll just ignore overflow possibility!
vec_data = new T[vec_size];
}
++length; // we are about to add an element
// we'll start at the end, sliding elements back until we find the
// place to insert the new element
int pos;
for( pos = length; pos > 0 && *val > *vec_data[pos - 1]; --pos )
{
vec_data[pos] = vec_data[pos - 1];
}
vec_data[pos] = val;
}
private:
T** vec_data;
int length;
int size;
};
有几个语法点需要特别注意:
1) 模板的参数仍然是T,但在声明中,在类名之后有一个T*。这告诉编译器来匹配任意类型的指针来选择这个指针模版,而不是通用模版。
2) T 是这个指针指向的类型,它自身不是一个指针。
例如,当你声明一个 sortedVector
注意,我们也可以在模版参数上进行局部特殊化-例如,你有一个固定长度的fixedVector向量类型,允许用户指定存储的数据类型,和向量的长度。这个向量的声明如下:
template
class fixedVector { ... };
那么你就可以用如下语法为布尔数据类型进行局部特殊化:
template
class fixedVector
注意:因为T已经不是模版参数,它就不被列在模版参数列表中,只留下length. 同时也请注意,length现在是fixedVector名字声明的一部分(在通用模版中,模版类名后没有任何东西)。顺便提一下,不要为一个模版参数不是一个完全的类型而感到诧异:模版参数是integer 变量,如unsigned length, 当然是有效的,而且有时是非常有用(参数特殊化).
最后你肯定会提出一个局部特殊化最终实现的细节问题: 如果同时有通用模版类,局部特殊化,和完全特殊化的情况下,编译器是如何选择正确的特殊化类呢?
通常的规则是编译器将选择最具体的模版特殊化类-(判断最具体的模版特殊化类就是他的模版参数可以被其他的模版声明类接受,但是它不能接受其他同名模版类可以接受的参数。)简单来说就是:先匹配完全特殊化,在局部特殊化,最后选择通用模版类。
例如,如果你决定定义一个sortedVector
template <>
class sortedVector
{
…
}
如果你声明sortedVector
原文请参考:http://www.cprogramming.com/tutorial/template_specialization.html