C++模板特化

模板特化

        在学习模板的时候我们用模板来解决了一个add模板函数,实现不同类型的传参相加,实践证明,模板函数比普通函数好用。那么现在如果我们要新增一个需求,就是如果传入的是两个string类型的参数,我们不要简单的拼接,我们要在两个字符串之间添加一个空格,显然模板函数已经无法满足我们的要求,解决方法就是使用模板特化,简单说就是模板的一个特殊化,当传参为两个string类型的时候,不调用模板函数,而是调用特化模板函数,传入其它类型的时候,仍然使用模板函数,如下示例:

template <typename T>
T add(T a, T b)
{
       return a+b;
}
template<> string add(string a,string b)
{
       string c=" ";
       return a+c+b;
}
int a=5,b=6;
string e="hu",f="daizhou";
cout<< add(a,b)<<endl;
cout<< add(e,f)<<endl;

模板特化的实现类似于函数重载,模板特化发生在编译时,而非运行时,所以效率高。

        特化的模板声明,前面一般是template<>,方括号为空。当同时出现同名、同参的普通函数和特化模板函数,及同名模板函数时,编译器优先使用普通函数,再是特化模板函数,最后才是模板函数。

全特化

        特化分为全特化和偏特化,全特化即把模板的所有参数都指定类型,示例如下,定义了一个类和他的全特化类,值得注意的是,泛化模板类的类名后面不需要类型,而特化模板需要在类名后面指定类型。

template<typename T1,typename T2>  //泛型模板,实体省略
template<> class peple<int,int>   //全特化
{
       public:
       void print(int a,int b)
       {
         cout<<"typename<>  a = "<< a <<"  b= "<< b <<endl;
        }
};

偏特化

        偏特化就是特化部分参数,如下示例,泛型模板2个参数都是模板,特化模板则将第二个参数固定,第一个模板仍然泛化。其实就是一个模板类中有多个参数类型,这些参数用到了模板参数,也用到了普通类型。

常规类型指定

template<typename T1,typename T2> class peple  //泛型模板,实体省略
template< typename T >  class peple<T,int>      //部分特化模板
{
public:
    void print(T a,int b)
    {
    cout<<"typename<>  a = "<< a <<"  b= "<< b <<endl;
    }
};

指针特化

        如下示例,将第二个参数固定为普通类型的指针,第一个参数仍然是泛化模板类型,无论是什么类型的特化,模板类的类型指定与内部函数的传参类型无直接关联,如下列中,第2个参数是指针,但内部的print函数可有可无形参,即使有,也可以不同。实际上这种偏特化完全可以将指针替换成引用,或者前面添加const,有const和没const的匹配是不同。

template<typename T>  class peple<T, double *>
{
  public:
     void print(int a,double b)
     {
       cout<<"typename<>  a = "<< a <<"  b= "<< b <<endl;
      }
};

模板偏特化模板

模板特化,指模板类的类型是另外一个模板类,如array、vector、list等。

template<typename T>  class peple<vector<T>>
{
public:
    void print(void)
    {
    cout<<"peple<T1 a,array<T2,int k>" << endl;
    }
}

int main(void)
{
peple<vector<int> > aa;
aa.print();
}

模板函数重载

        函数重载和模板函数重载有些类,特化的调用的特点是在函数后面用<>指定传参类型。有一个事实,就是模板函数仅支持全特化但是不支持偏特化,理由是因为函数重载先出现,而函数重载就已经能够实现函数偏特化的效果,一旦两个都支持,那就涉及优先级的问题,所以c++作者的选择是不允许函数模板偏特化。函数模板重载如下:

​template<typename T1>  void func(T1 a)
{
cout<< "func1 a= "<< a << endl;
}

template<typename T1>  void func(T1 *a)
{
cout<<"func2 *a= " << *a<<endl;
}
double c=5.6,d=7.8;
func<double>(c);
func<double *> (&d);//不支持偏特化

编译器匹配规则

第①步:匹配非模板函数,即普通函数,如果匹配到就执行,匹配不到进行下一步;

第②步:匹配基础泛化版本函数,匹配不到就报错,匹配到了再进入下一步。

第③步:匹配全特化函数,如果有就执行,无就执行上一步的基础泛化版本。

细节:模板函数的特化版本,不参与函数重载,即可以有完全特化版本的函数与普通函数同名、同参,在调用的时候,普通调用就是func(a);而泛化版本的调用时func<int>(a);

特化总结

        到此我们特化的研究就暂告于段落,特化实际上是我们帮编译器去做了类型推导的工作,告诉编译器不用去推导了,直接使用我给出的这个。

        模板特化和模板实例化两个概念注意区分,模板实例化指的是具体确定模板参数的类型T,模板特化就是前面一直在学的给出具体类型,本质上模板全特化就是一种实例化,而偏特化则不是,偏特化只是把模板类型的可能性压缩了,如模板偏特化成了指针,那么后面匹配的时候就只能是一个指针类型,因为模板函数并不是实例,普通函数是一个实例,所以他们不会冲突。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值