C++特化

来源:互联网 发布:magic mouse 知乎 编辑:程序博客网 时间:2024/06/10 01:32
最近看TCPL讲解有关template specialization 的问题,发现在讲解类模板特化对抑制代码的膨胀的问题的时候不是很清楚,自己也没有怎么看懂!最后通过查阅有关的文档,有所顿悟,不知道理解的正确不正确,先记下来再说。

为什么要特化模板呢?一个模板给出了一个单一的定义,用户可以用任何合法的参数去实例化模板参数。但是对写模板的人来说,他希望对于某些类型的模板参数,能够提供一种定义,对于其他类型的模板参数,给出模板的另外的定义。那么这个时候就要用到类模板的特化了。

在特化一个类模板之前,需要定义该类模板的一个通用的定义。例如:s
template<class  T> class stack
{
private:
    vector<T> v;
public:
    stack();
    stack(int);
    T& pop();
    T& top();
    void push(T&);
}

我们首先定义个带一个参数的stack模板,然后我们就可以特化这个模板了。
特化类模板的格式为:以template<>大头,后跟模板名,模板名字后面紧跟特化的模板参数,下面我们定义一个到void指针的stack特化版本
template<> stack<void*>
{
private:
    vector<void*> v;
public:
    stack();
    stack(int);
    void*& pop();
    void*& top();
    void push(void*&);
}
stack后面的<void>说明这个定义应该用在所有的T是void的stack实现里。

stack<void*>是一个完全的专门化,即我们在使用这个专门化的时候不需要描述其模板参数。
如果要定义一个专门化,使它能够并且只能够用于所有的指针stack,那么我们就需要定义一个部分专门化。
template<> stack<T*> : private stack<void*>
{
private:
    vector<T*> v;
private:
    stack();
    stack(int);
    T*& pop(){return static_cast<T*&>(stack<void*>::pop());}
    T*& top(){return static_cast<T*&>(stack<void*>::top());}
    void push(T*& p){stack<void*>::push(p);} 
}
这么特化stack模板以后,我们定义任何指针类型的stack,都将会选择stack<T*>这个特化来实例化。而不是之前定义的通用型的模板stack<T>来实例化。

我们这么做可以抑制代码的膨胀。因为若果没有stack<T*>特化,对于任何不同的T类型,例如我在声明类型为int*, double*,float*, char*的stack的时候,我们都将建立对应的副本,stack<int*>, stack<double*>, stack<float*>, stack<char*>,如果我们声明很多类型不同的stack,必定造成代码的膨胀。  
而如果对stack做了上面的特化,我们定义任指针类型的stack时,都只有stack的一个副本出现。(个人对于上面的实现是这么理解的,就是对于每一个指针类型stack的声明,都会有一个对应的用stack<T*>实例化的代码的副本存在,但是只有一份stack<void*>的副本存在,因为stack<T*>是从stack<void*>继承下来的,那么如果我们把stack的主要实现代码都放在stack<void*>里面实现的话,stack<T*>模板只定义一个界面,其中的实现调用模板stack<T*>里的函数,那么虽然每个类型的stack都会有一个副本,但是它的代码很少,因为它的每一个成员函数仅仅是对stack<void*>里成员函数的一次调用,主要的实现都放在了stack<void*>里面,而stack<void*>只有一个副本,这样就起到了抑制代码膨胀的效果。)