C++模板的特化(template specification)与偏特化(template partial specification)

作者: veaxen 分类: C/C++ 发布时间: 2017-07-24 16:26

模板是C++泛型编程中的重要部分,也是最复杂的部分,在C++中要了解template的出现在编程上给我们带来的方便性,我们先来了解下几个概念:1、模板实例化;2、模板特化;3、模板偏特化

模板实例化

与类实例化为对象对应,模板作为一个非常抽象的东西,生成一个具体的东西的动作称为实例化。当然这中实例化的区别有很多,对于类的示例化是发生在运行过程中的,通过构造函数进行实例化;对于模板,则是在编译的过程中,编译器通过“类型推导”进行实例化。而运行过程中,系统根本不需要,也不知道模板的概念。(从这里我们可以看出模板的一些好处,减轻编程的负担,让编译器帮助我们生成示例类/函数的代码,而且不把负担放在运行时,所以C++是一种重编译语言,但事实上,对于C++这样的语言,编译时间长短基本不影响整个软件的价值)

对于模板的实例化,我们可以类比宏的展开,编译器通过我们定义的对象,进行类型推断,从而根据模板类生成实际的代码,这点很想宏的展开,但是又是不同的。

模板特化

我们知道模板是为了让程序员能够脱离具体的数据类型开发出代码模板,在使用的时候有程序员提供具体的数据类型,然后由编译器进行实例化,这就要求所有的使用这个代码模板的数据类型所要跑的数据逻辑是一样的,当然通常我们就是希望这样(要不然我们就不需要模板了)。但是,10个数据类型,可能有一个数据类型,它对某个应用的逻辑过程要求就比较特别,进而不能使用通用的代码模板来进行限定,这个时候,你或许会说,“解决方法很简单,对于该特定的数据类型,我们不使用模板就是了,我们单独为它建立一个逻辑代码过程就可以了”。这是一个解决方法,但是之前你定义的模板是默认“可适应”该数据类型的,而软件开发过程中,你永远不要保证,在后来的程序员中(甚至是你自己)不会直接使用该模板,而不是特定的程序代码来完成特定的数据类型的操作。(庞大的软件开发的一个重要思想就是我们在较后期开发过程中,不强求对前面的所开发的东西牢记在心)。所以上面的解决方法不合理。
这就是模板特化的用处了。模板特化是指针对某个特定的类型,在定义的时候给出不同一般数据类型的逻辑实现。而在使用的时候,这个特殊性完全被屏蔽,你仍然只需要按照模板来使用,但是编译器会根据你之前的设定,给特别的数据类型以特定的代码逻辑。

模板偏特化

有了上面特化的基本概念,你会容易接受偏特化的思想。
偏特化,主要体现在,partial这个字上面,特化的意思是对某种数据类型“特殊化”的意思。而偏特化,自然就是对某种数据类型、或这说对这个模板“一点点的”、“不完整的”“特殊化”处理。
例如,你的模板类需要两个类型参数,而我们对第一类型参数为char类型的情况有特殊处理,这样不管第二个参数是什么类型,编译的时候所实例化的代码就是那个特殊的模板代码了。

再例如,如果你的模板需要一个类型参数(为了利于说明问题),但是,我们需要当类型为指针的情况下,需要有特殊的逻辑处理过程,例如,比较大小的时候,如果输入指针,则比较两个指针内容的大小是无意义的,这个特殊逻辑过程应该是比较两个指针指向的对象的大小而定的,这是一种常用的偏特化。

语法说明

首先我们先来定义一个通用的模板类,代码如下

template<class T1,class T2>
class A{
public:
    void print(){
        cout<<"This is a template class!"<<endl;
    }
}; 

当我们如下这样定义对象时,编译器就会帮我们生成相关的类代码

A<char,char> a1;

接下来,我们希望我们的A<int,int> a2;是不同与上面的模板类的逻辑的,那么就需要对模板类A进行特化:

template<>
class A<int,int>{
public:
    void print(){
        cout<<"This is a template specification class!"<<endl;
    }   
};

上面模板类A就是一个特化版本,当我们A<int,int> a2;时,编译器便会选中这个特化的模板进行实例化,而不会选中那个通用的模板类。

即使是有了特化模板,但我们有时还是需要这样特殊的需求,就是我们只对模板中的一个类型有要求,其它类型没有要求,也就是A<int,*> a3;这里*号代表任何类型,我们只对第一个个类型有要求,这时候就需要偏特化了。

template<class T2>
class A<int,T2>{
public:
    void print(){
        cout<<"This is a template partial specification class!"<<endl;
    }
};

这就是模板的偏特化了,只对其中某些类型有要求,其它就没有要求了。

下面给出测试代码:

int main()
{
    A<char,char> a1;
    A<int,int> a2;
    A<int,char> a3;

    a1.print();
    a2.print();
    a3.print();

    return 0;
}

输出为:
This is a template class!
This is a template specification class!
This is a template partial specification class!

最后,关于模板函数,严格来说,函数模板不支持偏特化,但由于可以对函数进行重载,所以可以达到类似与模板偏特化的效果

template<class T> void f(T); —–(1)
根据重载规则,对(1)进行重载
template<class T> void f(T*);—–(2)
如果将(1)称为基模板,那么(2)称为对基模板的重载,而非对(1)的偏特化。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

一条评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据