C++ 没有办法限制类型参数的范围,我们可以使用任意一种类型来实例化模板。但是模板中的语句(函数体或者类体)不一定就能适应所有的类型,可能会有个别的类型没有意义,或者会导致语法错误。

例如有下面的函数模板,它用来获取两个变量中较大的一个:
template<class T> const T& Max(const T& a, const T& b){
    return a > b ? a : b;
}
请读者注意a > b这条语句,>能够用来比较 int、float、char 等基本类型数据的大小,但是却不能用来比较结构体变量、对象以及数组的大小,因为我们并没有针对结构体、类和数组重载>。

另外,该函数模板虽然可以用于指针,但比较的是地址大小,而不是指针指向的数据,所以也没有现实的意义。

除了>,+-*/==<等运算符也只能用于基本类型,不能用于结构体、类、数组等复杂类型。总之,编写的函数模板很可能无法处理某些类型,我们必须对这些类型进行单独处理。

模板是一种泛型技术,它能接受的类型是宽泛的、没有限制的,并且对这些类型使用的算法都是一样的(函数体或类体一样)。但是现在我们希望改变这种“游戏规则”,让模板能够针对某种具体的类型使用不同的算法(函数体或类体不同),这在 C++ 中是可以做到的,这种技术称为模板的显示具体化(Explicit Specialization)。

函数模板和类模板都可以显示具体化,下面我们先讲解函数模板的显示具体化,再讲解类模板的显示具体化。
函数模板的显示具体化

在讲解函数模板的显示具体化语法之前,我们先来看一个显示具体化的例子:
#include <iostream>
#include <string>
using namespace std;
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);
//重载<<
ostream & operator<<(ostream &out, const STU &stu);
int main(){
    int a = 10;
    int b = 20;
    cout<<Max(a, b)<<endl;
   
    STU stu1 = { "王明", 16, 95.5};
    STU stu2 = { "徐亮", 17, 90.0};
    cout<<Max(stu1, stu2)<<endl;
    return 0;
}
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;
}
ostream & operator<<(ostream &out, const STU &stu){
    out<<stu.name<<" , "<<stu.age <<" , "<<stu.score;
    return out;
}
运行结果:
20
王明 , 16 , 95.5

本例中,STU 结构体用来表示一名学生(Student),它有三个成员,分别是姓名(name)、年龄(age)、成绩(score);Max() 函数用来获取两份数据中较大的一份。

要想获取两份数据中较大的一份,必然会涉及到对两份数据的比较。对于 int、float、char 等基本类型的数据,直接比较它们本身的值即可,而对于 STU 类型的数据,直接比较它们本身的值不但会有语法错误,而且毫无意义,这就要求我们设计一套不同的比较方案,从语法和逻辑上都能行得通,所以本例中我们比较的是两名学生的成绩(score)。

不同的比较方案最终导致了算法(函数体)的不同,我们不得不借助模板的显示具体化技术对 STU 类型进行单独处理。第 14 行代码就是显示具体化的声明,第 34 行代码进行了定义。

请读者注意第 34 行代码,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);
函数的调用规则

回顾一下前面学习到的知识,在 C++ 中,对于给定的函数名,可以有非模板函数、模板函数、显示具体化模板函数以及它们的重载版本,在调用函数时,显示具体化优先于常规模板,而非模板函数优先于显示具体化和常规模板。
类模板的显示具体化

除了函数模板,类模板也可以显示具体化,并且它们的语法是类似的。

在《C++类模板》一节中我们定义了一个 Point 类,用来输出不同类型的坐标。在输出结果中,横坐标 x 和纵坐标 y 是以逗号,为分隔的,但是由于个人审美的不同,我希望当 x 和 y 都是字符串时以|为分隔,是数字或者其中一个是数字时才以逗号,为分隔。为了满足我这种奇葩的要求,可以使用显示具体化技术对字符串类型的坐标做特殊处理。

下面的例子演示了如何对 Point 类进行显示具体化:
#include <iostream>
using namespace std;
//类模板
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度

请读者注意第 25 行代码,Point<char*, char*>表明了要将类型参数 T1、T2 都具体化为char*类型,原来使用 T1、T2 的位置都应该使用char*替换。Point 类有两个类型参数 T1、T2,并且都已经被具体化了,所以整个类模板就不再有类型参数了,模板头应该写作template<>。

再来对比第 19、40 行代码,可以发现,当在类的外部定义成员函数时,普通类模板的成员函数前面要带上模板头,而具体化的类模板的成员函数前面不能带模板头。
部分显式具体化

在上面的显式具体化例子中,我们为所有的类型参数都提供了实参,所以最后的模板头为空,也即template<>。另外 C++ 还允许只为一部分类型参数提供实参,这称为部分显式具体化。

部分显式具体化只能用于类模板,不能用于函数模板。

仍然以 Point 为例,假设我现在希望“只要横坐标 x 是字符串类型”就以|来分隔输出结果,而不管纵坐标 y 是什么类型,这种要求就可以使用部分显式具体化技术来满足。请看下面的代码:
#include <iostream>
using namespace std;
//类模板
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度

本例中,T1 对应横坐标 x 的类型,我们将 T1 具体化为char*,第 25 行代码就是类模板的部分显示具体化。

模板头template<typename T2>中声明的是没有被具体化的类型参数;类名Point<char*, T2>列出了所有类型参数,包括未被具体化的和已经被具体化的。

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

C++ 模板的显示具体化相关推荐

  1. c++中函数模板的显示具体化

    函数模板的显示具体化. 函数模板存在局限性,当定义函数模板时,我们始终假定函数体中的语句是合法的.例如定义如下函数模板: template <typename T> T add(T x, ...

  2. c++模板显示实例化,显示具体化,隐式实例化

    函数模板是C++新增的一种性质,它允许只定义一次函数的实现,即可使用不同类型的参数来调用该函数.这样做可以减小代码的书写的复杂度,同时也便于修改(注:使用模板函数并不会减少最终可执行程序的大小,因为在 ...

  3. VMware VCenter模板不显示问题

    背景: 最近公司机房新上两台ESXI5.5主机,我在Vcenter上把原来的主机移除后,重新添加了所有的主机. 原来建立的系统模板,不显示了或者找不到?? 模板:可以快速克隆系统. 分析: 1.   ...

  4. laytpl语法_浅谈laytpl 模板空值显示null的解决方法及简单的js表达式

    浅谈laytpl 模板空值显示null的解决方法及简单的js表达式 laytpl 模板语法 {{ d.field }} 输出一个普通字段,不转义html 官方的说明 但d.field 为空时会显示nu ...

  5. NC开发笔记——单据模板编辑显示公式使用

    单据模板编辑显示公式使用 (一) 公式使用场景 用户使用产品时,往往对单据上的字段取值有各种不同的需求.为此单据模板提供 了模板公式功能,可以让实施顾问或者用户通过配置各种公式,并且不用修改代码,从 ...

  6. C++模板元编程(3)模板显示具体化

    前面我们介绍了模板隐式实例化和显示实例化,今天介绍另外一个截然不同的概念,模板具体化. 来说说模板遇到的问题,也就是为什么要有模板具体化方法.模板函数自身有一定的局限性.由于其采用对对象类型的泛化处理 ...

  7. ecshop入门第一步,替换ecshop模板的显示图片

    所有的都是模范默认模板来制作,所以应该参考默认模板的一些数据设置 1.找到themes\default\images文件夹下 screenshot.png 图片 默认如下: 在你的模板文件夹下新建一个 ...

  8. jaspersoft studio创建jasperreport模板 summary显示page header(页眉)

    使用jaspersoft studio创建模板,page header在每页都显示,但是当这页只有summary部分时,就不显示page header. 解决方法:强制让page header在sum ...

  9. Latex编译IEEE会议模板字体显示异常的解决方法

    问题描述 在IEEE的网站上下载的Latex的Template:https://www.ieee.org/conferences/publishing/templates.html 在Latex中运行 ...

最新文章

  1. 算法基础知识科普:8大搜索算法之AVL树(上)
  2. JVM 配置常用参数
  3. Rspamd 将收到的spam/ham转了到指定邮箱
  4. EntityFramework Core 1.1有哪些新特性呢?我们需要知道
  5. 软件测试推荐专业,软件测试专业老师推荐信
  6. 03-映射文件的sql语句中 #{} 和 ${} 的区别以及实现模糊查询
  7. servu无法显示远程文件夹_win10软件:解决文件资源管理器无法显示文件夹大小...
  8. mysql用大白话解释_大白话 golang 教程-22-关系型数据库访问
  9. [0715]Jsoi Test sequence
  10. linux rto 参数设置,linux 如何计算RTO值
  11. 移远BC95 nbiot接入华为OC云平台(电信云)
  12. 土方工程量计算表格excel_土方方格网计算表格excel.xls
  13. 徐思201771010132《面向对象程序设计(java)》第七周学习总结
  14. 一清机、二清机、跳码,你知道这些POS机猫腻的原理吗?
  15. Linux如何在屏幕上显示ASCII/中文字符
  16. 什么是系统时钟?什么是时钟系统?时钟系统有什么作用?
  17. underscorejs-pluck学习
  18. 批处理使用技巧:批量修改文件名
  19. 二十世纪初的心理学时间轴制作
  20. 齐岳多吡啶萘酰亚胺荧光树形分子(PDPN),三萘嵌二苯二酰亚胺类近红外有机光功能分子定制,4-氨基-1, 8-萘酰亚胺类化合物

热门文章

  1. 第六章:详细设计。盒图、问题分析图即PAD图、过程设计语言PDL伪码
  2. 程序包清单签名验证失败
  3. 不同PyTorch版本训练同一个代码结果差异巨大
  4. 关于共享自习室查询座位空闲状态的算法笔记
  5. 为什么你不能体面地做自己
  6. OTU/ASV/Feature tabel 表格 过滤 相对丰度 微生物
  7. “马赛克”真能去除了?老司机狂喜!
  8. 微信小程序项目图片如何保存到本地的方法
  9. Javascript Yielding Processes 定时器数组分块技术
  10. 编写一个购物车程序,实现在界面中以列表的形式显示购物车的商品信息,商品信息包括商品名称、价格和数量功能,并能够对购物车中的商品信息进行增删改查