模版特殊化和局部模版特殊化(偏特化)

模板特殊化

在使用模版的过程中,大部分情况下,我们都会为所有可能的数据类型写一个通用的模版类。 而模版特殊化的思路就是继承默认的模版实现,为一个特殊的类型实现不同的处理方式。

让我们看个例子,向量,大多数情况下它都是由给定的数据类型的数组实现。 现在我们要实现一个bool类型的向量,为了节省内存,我们可以用一个integer的每一个bit来表示bool值,这样就可以用一个integer向量来实现bool类型向量。如何实现呢? 请看下面两个不同的向量类。

第一个类如下:

template <typename T>
class vector
{
// accessor functions and so forth
private:
    T* vec_data; // we'll store the data as block of dynamically allocated
// memory
    int length; // number of elements used
    int vec_size; // actual size of vec_data
};

如用上面的类来实现bool向量,你知道布尔类型的数据长度在大多数的系统中都是8bits,尽管我们只用其中的一个bit。 如果一个integer数据的每一个bit都可以人为控制,我们就可以考虑使用integer数组来实现bool向量。

要实现这个功能,我们需要对着类似于模版的类进行特殊说明,但是这个类中,所有模版参数的列表为空:

template <>

而且具体的数据类型紧随类名之后:class classname<type>. 在目前的例子中,模版的实现如下:我们将用无符号整形数组存储bool值。
template <>
class vector <bool>
{
    // interface
    private:
      unsigned int *vector_data;
      int length;
      int size;
};

注意:这特殊化的向量类和通用的向量类已经是完全不同的两个类了,尽管他们有相同的vector模版类名。他们不共享任何的接口或者任何代码。

在此,我们需要再次重申为什么需要特殊化。在上述的例子中,主要的原因是提高实现的空间使用效率。但是我们可以想一想我们迟早都会用到这一功能的情况,比如,你想为某一特定类型的模版类添加额外的方法,而这些方法并不适用于其它数据类型。如你想为double型向量添加个返回每一double值的非整形部分的方法。 尽管你可能更喜欢用继承。在这里使用模板特殊化不妨也是一个办法。这样一来,所有的double模板类型实例都添加了这个方法。

上述是对某一个类型的模板类进行特殊化,如果你想特殊化某几个模板时,例如,你有一些sortedVector模板类型需要定义“>”操作符,而另外一组类型不包含任何重载运算符,但是包含一个比较函数,你可能需要特殊化你的模板来分别处理这些类。

模板局部特殊化

局部模板特殊化源于和上述模板特殊化一样的动机。 可是这次,除了为一个特殊的类型实现一个类外,我们最终实现一个仍然允许其他参数的模版。让我们用一个具体的例子来说明这个功能。

让我们回到刚才sortVector的例子。 我们需要考虑排序中做比较操作的方式:使用”>”操作符如果它已经被实现,或者特殊化如果它没有被实现。但是现在,如果我们想在sortVector中保存指向对象的指针类型数据。 我们可以使用标准的 “>”比较指针的值来给他们排序。 (此例中,我们的向量是由低到高排序):

template <typename T>
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 <typename T>
class sortedVector<T *>
{
    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<int*>, T 就是int 类型。 你可以这样认为,星号(“*”)之后的类型就是T. 这就意味着在我们的实现中需要十分仔细。注意上面的代码,vec_data是一个T**,这是因为我们需要动态的数组大小来存储指针值。

注意,我们也可以在模版参数上进行局部特殊化-例如,你有一个固定长度的fixedVector向量类型,允许用户指定存储的数据类型,和向量的长度。这个向量的声明如下:

template <typename T, unsigned length>
class fixedVector { ... };

那么你就可以用如下语法为布尔数据类型进行局部特殊化:

template <unsigned length>
class fixedVector<bool, length> {...}

注意:因为T已经不是模版参数,它就不被列在模版参数列表中,只留下length. 同时也请注意,length现在是fixedVector名字声明的一部分(在通用模版中,模版类名后没有任何东西)。顺便提一下,不要为一个模版参数不是一个完全的类型而感到诧异:模版参数是integer 变量,如unsigned length, 当然是有效的,而且有时是非常有用(参数特殊化).

最后你肯定会提出一个局部特殊化最终实现的细节问题: 如果同时有通用模版类,局部特殊化,和完全特殊化的情况下,编译器是如何选择正确的特殊化类呢?

通常的规则是编译器将选择最具体的模版特殊化类-(判断最具体的模版特殊化类就是他的模版参数可以被其他的模版声明类接受,但是它不能接受其他同名模版类可以接受的参数。)简单来说就是:先匹配完全特殊化,在局部特殊化,最后选择通用模版类。
例如,如果你决定定义一个sortedVector<int *>,它是以内存的位置信息来排序,你可以创建一个完全特殊化类 sortedVector:

template <>
class sortedVector<int *>
{

}

如果你声明sortedVector<int *>, 那么,编译器将选择这个全特殊化类,而不是局部特殊化的类。

原文请参考:http://www.cprogramming.com/tutorial/template_specialization.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一些关于模板特化的高级面试问题: 1. 什么是模板特化? 模板特化是一种针对特定类型的模板实例化方式,它可以对某些类型进行特殊处理。模板特化的语法形式为template <> class template-name <arguments>, 其中template <>表示模板特化,template-name表示所要特化的模板名称,arguments表示所要特化的模板参数。 2. 什么是特化与全特化的区别? 全特化是对模板的所有参数进行特化,而特化是只对部分参数进行特化。在全特化中,所有的模板参数都需要进行特化;而在特化中,只有部分模板参数需要进行特化。 3. 特化可以针对哪些类型进行特化特化可以针对任意类型进行特化,包括基本类型、自定义类型、指针类型、引用类型等。 4. 特化可以用于哪些应用场景? 特化可以用于解决一些特定的问题,比如在编译期间对类型进行分类、过滤等,以及实现一些高级的编程技巧,比如类型转换、类型推导等。特化还可以用于实现一些高级的数据结构,比如树、图等。 5. 特化是否可以继承模板的基本实现? 特化可以继承模板的基本实现,但是特化的实现需要与基本模板的实现有所不同。具体来说,特化需要重新定义模板参数,并且只对需要特化的部分进行处理。同时,特化也可以继承基本模板的实现,以实现代码复用等目的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值