C++ 模板的显式具体化

  本文结合网上的资料对C++模板的显式具体化做了一个总结。

  对于模板,模板中的语句(函数体或者类)不一定能适应所有的类型,可能会有个别的类型没有意义,或者会导致语法错误。我们希望模板能够针对某种具体的类型使用不同的算法,可以使用显式具体化。

函数模板的显式具体化

typedef struct{
    string name;
    int age;
    float score;
} STU;
//函数模板
template<class T> const T& Max(const T& a, const T& b);
//函数模板的显示具体化(针对STU类型的显示具体化)
template<> const STU& Max<STU>(const STU& a, const STU& b);

template<class T> const T& Max(const T& a, const T& b){
    return a > b ? a : b;
}
template<> const STU& Max<STU>(const STU& a, const STU& b){
    return a.score > b.score ? a : b;
}

    Max<STU>中的STU表明了要将类型参数T具体化为STU类型,原来使用T的位置都应该使用STU替换,包括返回值类型、形参类型、局部变量的类型。Max只有一个类型参数T,并且已经被具体化为STU了,这样整个模板就不再有类型参数了,类型参数列表也就为空了,所以模板头应该写作template<>。另外,Max<STU>中的STU是可选的,因为函数的形参已经表明,这是 STU 类型的一个具体化,编译器能够逆推出 T 的具体类型。简写后的函数声明为:

template<> const STU& Max(const STU& a, const STU& b);

  在调用函数时,显示具体化优先于常规模板,而非模板函数优先于显示具体化和常规模板。

类模板的显式具体化

 如果一个可以输出不同类型坐标的类模板,希望当 x 和 y 都是字符串时以|为分隔,是数字或者其中一个是数字时才以逗号,为分隔,可以使用显示具体化技术对字符串类型的坐标做特殊处理。

//类模板
template<class T1, class T2> class Point{
public:
    Point(T1 x, T2 y): m_x(x), m_y(y){ }
public:
    T1 getX() const{ return m_x; }
    void setX(T1 x){ m_x = x; }
    T2 getY() const{ return m_y; }
    void setY(T2 y){ m_y = y; }
    void display() const;
private:
    T1 m_x;
    T2 m_y;
};
template<class T1, class T2>  //这里要带上模板头
void Point<T1, T2>::display() const{
    cout<<"x="<<m_x<<", y="<<m_y<<endl;
}
//类模板的显示具体化(针对字符串类型的显示具体化)
template<> class Point<char*, char*>{
public:
    Point(char *x, char *y): m_x(x), m_y(y){ }
public:
    char *getX() const{ return m_x; }
    void setX(char *x){ m_x = x; }
    char *getY() const{ return m_y; }
    void setY(char *y){ m_y = y; }
    void display() const;
private:
    char *m_x;  //x坐标
    char *m_y;  //y坐标
};
//这里不能带模板头template<>
void Point<char*, char*>::display() const{
    cout<<"x="<<m_x<<" | y="<<m_y<<endl;
}
int main(){
    ( new Point<int, int>(10, 20) ) -> display();
    ( new Point<int, char*>(10, "东京180度") ) -> display();
    ( new Point<char*, char*>("东京180度", "北纬210度") ) -> display();
    return 0;
}

运行结果:

x=10, y=20
x=10, y=东京180度
x=东京180度 | y=北纬210度

  Point<char*, char*>表明了要将类型参数 T1、T2 都具体化为char*类型,原来使用 T1、T2 的位置都应该使用char*替换。Point 类有两个类型参数 T1、T2,并且都已经被具体化了,所以整个类模板就不再有类型参数了,模板头应该写作template<>。当在类的外部定义成员函数时,普通类模板的成员函数前面要带上模板头,而具体化的类模板的成员函数前面不能带模板头。

部分显式具体化

  C++ 还允许只为一部分类型参数提供实参,这称为部分显式具体化。部分显式具体化只能用于类模板,不能用于函数模板。

  仍然以 Point 为例,假设我现在希望“只要横坐标 x 是字符串类型”就以|来分隔输出结果,而不管纵坐标 y 是什么类型,这种要求就可以使用部分显式具体化技术来满足:

//类模板
template<class T1, class T2> class Point{
public:
    Point(T1 x, T2 y): m_x(x), m_y(y){ }
public:
    T1 getX() const{ return m_x; }
    void setX(T1 x){ m_x = x; }
    T2 getY() const{ return m_y; }
    void setY(T2 y){ m_y = y; }
    void display() const;
private:
    T1 m_x;
    T2 m_y;
};
template<class T1, class T2>  //这里需要带上模板头
void Point<T1, T2>::display() const{
    cout<<"x="<<m_x<<", y="<<m_y<<endl;
}
//类模板的部分显示具体化
template<typename T2> class Point<char*, T2>{
public:
    Point(char *x, T2 y): m_x(x), m_y(y){ }
public:
    char *getX() const{ return m_x; }
    void setX(char *x){ m_x = x; }
    T2 getY() const{ return m_y; }
    void setY(T2 y){ m_y = y; }
    void display() const;
private:
    char *m_x;  //x坐标
    T2 m_y;  //y坐标
};
template<typename T2>  //这里需要带上模板头
void Point<char*, T2>::display() const{
    cout<<"x="<<m_x<<" | y="<<m_y<<endl;
}
int main(){
    ( new Point<int, int>(10, 20) ) -> display();
    ( new Point<char*, int>("东京180度", 10) ) -> display();
    ( new Point<char*, char*>("东京180度", "北纬210度") ) -> display();
    return 0;
}

运行结果:
x=10, y=20
x=东京180度 | y=10
x=东京180度 | y=北纬210度

  模板头template<typename T2>中声明的是没有被具体化的类型参数;类名Point<char*, T2>列出了所有类型参数,包括未被具体化的和已经被具体化的。类名后面之所以要列出所有的类型参数,是为了让编译器确认“到底是第几个类型参数被具体化了”,如果写作template<typename T2> class Point<char*>,编译器就不知道char*代表的是第一个类型参数,还是第二个类型参数。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值