浅谈C++模版特化问题

本文介绍了C++中的模板特化,包括类模板和函数模板的全特化与偏特化。通过示例,解释了模板匹配优先级:全特化 > 偏特化 > 普通模板。讨论了类模板的偏特化如何提高实例化匹配度,以及函数模板虽不允许偏特化,但可以通过重载实现类似效果。最后,展示了不同模板调用方式及其输出结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C++中有类模版和函数模版2种模版方式,模版特化也分为全特化和偏特化2种。

首先来看下类模版

#include <iostream>

//普通模版
template <typename T1,typename T2>
class Base{
private:
    T1 n1;
    T2 n2;
public:
    Base(T1 n1_t,T2 n2_t):n1(n1_t),n2(n2_t){}
    void show()const{
        std::cout << "#1:" << n1 << "," << n2 << std::endl;
    }
};

//模版偏特化
template <typename T2>
class Base<char,T2>{
private:
    char n1;
    T2 n2;
public:
    Base(char n1_t,T2 n2_t):n1(n1_t),n2(n2_t){}
    void show()const{
        std::cout << "#2:" << n1 << "," << n2 << std::endl;
    }
};

//模版全特化
template <>
class Base<double,double>{
private:
    double n1;
    double n2;
public:
    Base(double n1_t,double n2_t):n1(n1_t),n2(n2_t){}
    void show()const{
        std::cout << "#3:" << n1 << "," << n2 << std::endl;
    }
};

int main(){
    using namespace std;

    Base<char,int> s1('A',11);            //通过偏特化的模版进行实例化
    Base<int,int> s2(20,20);              //通过普通模版进行实例化
    Base<double,double> s3(13.5,26.5);    //通过全特化的模版进行实例化
    s1.show();
    s2.show();
    s3.show();

    return 0;
}

相信大家对普通模版的定义形式已经很熟悉了,这里说下模版特化时候的定义形式。

模版特化又是什么呢?有什么作用呢?模版特化又叫模版具体化,是指模板参数在某种特定类型下的具体实现。用于提升模版实例化时匹配模版的优先级的。

在模版中匹配优先级:模版全特化 > 模版偏特化 > 普通模版

模版偏特化是相对于现有模版进行部分具体化的,用于将原模版参数列表中某些通用参数指定为特定类型。如上代码中将模版参数列表中的T1指定为特定类型的char,第二个模版参数T2没有进行指定,当需要实例化一个类模版时,如果指定的模版第一个参数类型是char的话,将优先使用该偏特化版本进行实例化,因为匹配度比普通模版要高,当然这是在没有某些全特化版本的情况下,因为全特化匹配了,优先程度高于偏特化和普通模版。

如上代码所示,s1对象实例化时提供了一个char和一个int类型,第一个类型刚好与Base模版的偏特化相匹配,所以优先采用该偏特化的版本进行实例化对象,所以s1.show()将输出#2的语句。

s2对象提供的是俩个int类型,这与模版全特化版本和偏特化版本均不匹配 所以将采用普通模版进行实例化,s2.show()所以输出#1的语句。

s3对象提供的是俩个double类型,这与模版全特化匹配,所以采用该模版的全特化版本进行实例化,s3.show()输出#3的语句。

上面代码执行后输出:

#2:A,11
#1:20,20
#3:13.5,26.5

函数模版的定义形式与类模版类似,只是目前函数模版不允许偏特化,只允许全特化,但函数模版可以通过函数模版的重载来实现“偏特化”。代码:

//普通函数模版 
template<typename T1, typename T2>  
void func(const T1& a , const T2& b){  
    std::cout << "#1:" << a << "," << b << std::endl;
}  

//函数模版全特化  
template<>  
void func<int ,char >(const int& a, const char& b){  
    std::cout << "#2:" << a << "," << b << std::endl;
}  

/*
//错误! 函数模版不存在偏特化形式
template <typename T2>
void func<double,T2>(const double& a,const T2& b){
    std::cout << "#3:" << a << "," << b << std::endl;
}
*/

//利用函数模版的重载来实现“偏特化”
template <typename T2>
void func(const double& a,const T2& b){
    std::cout << "#4:" << a << "," << b << std::endl;
}

int main(){
    using namespace std;

    func<int,int>(13,20);   //$1
    func(66,'T');           //$2
    func<int>(96.5,10);     //$3

    return 0;
}
函数模版的定义形式与类模版相似,只是函数模版不存在偏特化,只有全特化,所以如果按照类模版方式进行声明偏特化版本的话编译器是不允许的。当然可以通过函数模版的重载进行变相实现“偏特化”效果,但并不是说函数模版就有偏特化。

上面代码$1函数中将俩个int类型作为函数模版的类型参数,因为不存在匹配的特化版本,所以编译器会采用普通函数模版来定义函数,并让程序调用,从而$2调用并输出#1语句。到这里相信大家已经发现了 $2和其他模版函数调用形式不同,它没有指定模版参数的类型,这也是允许的(仅对函数模版)编译器允许模版函数进行调用时可以不显示指定参数类型,如果没有指定,则编译器会从函数参数列表中进行解析类型,则$2的调用形式相当于func<int,char>(66,'T'); 而类模版不允许这么做。所以$2的参数类型与函数模版的全特化版本匹配,编译器则会定义模版的全特化版本并由程序调用,输出#2语句,$3因为与函数模版的重载形式匹配程度高于普通模版,所以调用并输出#4语句。

上面函数模版部分的程序输出:

#1:13,20
#2:66,T
#4:96.5,10

以上就是对模版中的特化问题进行的浅谈,如有问题,还请大家指出来,共同进步。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值