模板的实例化指函数模板(类模板)生成模板函数(模板类)的过程。对于函数模板而言,模板实例化之后,会生成一个真正的函数。而类模板经过实例化之后,只是完成了类的定义,模板类的成员函数需要到调用时才会被初始化。模板的实例化分为隐式实例化和显示实例化。

对函数模板的使用而言,分为两种调用方式,一种是显示模板实参调用(显示调用),一种是隐式模板实参调用(隐式调用)。对于类模板的使用而言,没有隐式模板实参和显式模板实参使用的说法,因为类模板的使用必须显示指明模板实参。各个概念请勿混淆。

1.隐式实例化

1.1模板隐式实例化的定义

这是相对于模板显示实例化而言。在使用模板函数和模板类时,不存在指定类型的模板函数和模板类的实体时,由编译器根据指定类型参数隐式生成模板函数或者模板类的实体称之为模板的隐式实例化。

1.2函数模板隐式实例化

函数模板隐式实例化指的是在发生函数调用的时候,如果没有发现相匹配的函数存在,编译器就会寻找同名函数模板,如果可以成功进行参数类型推演,就对函数模板进行实例化。

还有一种简介调用函数的情况,也可以完成函数模板的实例化。所谓的简介调用是指将函数入口地址传给一个函数指针,通过函数指针完成函数调用。如果传递给函数指针不是一个真正的函数,那么编译器就会寻找同名的函数模板进行参数推演,进而完成函数模板的实例化。参考如下示例。

#include <iostream>
using namespace std;
template <typename T> void func(T t){
cout<<t<<endl;
} void invoke(void (*p)(int)){
int num=10;
p(num);
}
int main(){
invoke(func);
}

程序成功运行并输出10。

1.3类模板隐式实例化

类模板隐式实例化指的是在使用模板类时才将模板实例化,相对于类模板显示实例化而言的。考察如下程序。

#include <iostream>
using namespace std;
template<typename T>class A{
T num;
public:
A(){
num=T(6.6);
}
void print(){
cout<<"A'num:"<<num<<endl;
}
}; int main(){
A<int> a; //显示模板实参的隐式实例化
a.print();
}

程序输出结果:A’num:6。


2. 显示实例化

2.1模板显示实例化的定义

显示实例化也称为外部实例化。在不发生函数调用的时候讲函数模板实例化,或者在不适用类模板的时候将类模板实例化称之为模板显示实例化。

2.2函数模板的显示实例化

对于函数模板而言,不管是否发生函数调用,都可以通过显示实例化声明将函数模板实例化,格式为:

template [函数返回类型] [函数模板名]<实际类型列表>(函数参数列表)

例如:

template void func<int>(const int&);

2.3类模板的显示实例化

对于类模板而言,不管是否生成一个模板类的对象,都可以直接通过显示实例化声明将类模板实例化,格式为:

template class [类模板名]<实际类型列表>

例如:

template class theclass<int>;

3.函数模板调用方式

3.1隐式模板实参调用

在发生函数模板的调用时,不显示给出模板参数而经过参数推演,称之为函数模板的隐式模板实参调用(隐式调用)。如:

template <typename T> void func(T t){
cout<<t<<endl;
}
func(5);//隐式模板实参调用

3.2显示模板实参调用

在发生函数模板的调用时,显示给出模板参数而不需要经过参数推演,称之为函数模板的显示模板实参调用(显示调用)。

显示模板实参调用在参数推演不成功的情况下是有必要的。考察如下程序。

#include <iostream>
using namespace std;
template <typename T> T Max(const T& t1,const T& t2){
return (t1>t2)?t1:t2;
} int main(){
int i=5;
//cout<<Max(i,'a')<<endl; //无法通过编译
cout<<Max<int>(i,'a')<<endl; //显示调用,通过编译
}

直接采用函数调用Max(i,’a’)会产生编译错误,因为i和’a’具有不同的数据类型,无法从这两个参数中进行类型推演。而采用Max< int>(i,’a’)调用后,函数模板的实例化不需要经过参数推演,而函数的第二个实参也可以由char转换为int型,从而成功完成函数调用。

编程过程中,建议采用显示模板实参的方式调用函数模板,这样提高了代码的可读性,便于代码的理解和维护。


4.模板特化

4.1模板特化的定义

模板特化不同于模板的实例化,模板参数在某种特定类型下的具体实现称为模板的特化。模板特化有时也称之为模板的具体化,分别有函数模板特化和类模板特化。

4.2函数模板特化

函数模板特化是在一个统一的函数模板不能在所有类型实例下正常工作时,需要定义类型参数在实例化为特定类型时函数模板的特定实现版本。查看如下例子。

#include <iostream>
using namespace std; template<typename T> T Max(T t1,T t2){
return (t1>t2)?t1:t2;
} typedef const char* CCP;
template<> CCP Max<CCP>(CCP s1,CCP s2){
return (strcmp(s1,s2)>0)?s1:s2;
} int main(){
//调用实例:int Max<int>(int,int)
int i=Max(10,5);
//调用显示特化:const char* Max<const char*>(const char*,const char*)
const char* p=Max<const char*>("very","good");
cout<<"i:"<<i<<endl;
cout<<"p:"<<p<<endl;
}

程序正常编译运行结果: 
i:10 
p:very

在函数模板显示特化定义(Explicit Specialization Definition)中,显示关键字template和一对尖括号<>,然后是函数模板特化的定义。该定义指出了模板名、被用来特化模板的模板实参,以及函数参数表和函数体。在上面的程序中,如果不给出函数模板Max< T>在T为const char*时的特化版本,那么在比较两个字符串的大小时,比较的是字符串的起始地址的大小,而不是字符串的内容在字典序中先后次序。

4.2.1使用函数重载替代函数模板特化

除了定义函数模板特化版本外,还可以直接给出模板函数在特定类型下的重载形式(普通函数)。使用函数重载可以实现函数模板特化的功能,也可以避免函数模板的特定实例的失效。例如,把上面的模板特化可以改成如下重载函数。

typedef const char* CCP;
CCP Max(CCP s1,CCP s2){
return (strcmp(s1,s2)>0)?s1:s2;
}

程序运行结果和使用函数模板特化相同。但是,使用普通函数重载和使用模板特化还是有不同之处,主要表现在如下两个方面: 
(1)如果使用普通重载函数,那么不管是否发生实际的函数调用,都会在目标文件中生成该函数的二进制代码。而如果使用模板的特化版本,除非发生函数调用,否则不会在目标文件中包含特化模板函数的二进制代码。这符合函数模板的“惰性实例化”准则。

(2)如果使用普通重载函数,那么在分离编译模式下,应该在各个源文件中包含重载函数的申明,否则在某些源文件中就会使用模板函数,而不是重载函数。

4.3类模板特化

类模板特化类似于函数模板的特化,即类模板参数在某种特定类型下的具体实现。考察如下代码。

#include <iostream>
using namespace std; template<typename T>class A{
T num;
public:
A(){
num=T(6.6);
}
void print(){
cout<<"A'num:"<<num<<endl;
}
}; template<>class A<char*>{
char* str;
public:
A(){
str="A' special definition ";
}
void print(){
cout<<str<<endl;
}
}; int main(){
A<int> a1; //显示模板实参的隐式实例化
a1.print();
A<char*> a2; //使用特化的类模板
A2.print();
}

程序输出结果如下: 
A’num:6 
A’ special definition

总结:

隐式实例化指的是:在使用模板之前,编译器不生成模板的声明和定义实例。只有当使用模板时,编译器才根据模板定义生成相应类型的实例。如:int i=0, j=1;swap(i, j);  //编译器根据参数i,j的类型隐式地生成swap<int>(int &a, int &b)的函数定义。Array<int> arVal;//编译器根据类型参数隐式地生成Array<int>类声明和类函数定义。

显式实例化:当显式实例化模板时,在使用模板之前,编译器根据显式实例化指定的类型生成模板实例。如前面显示实例化(explicit instantiation)模板函数和模板类。其格式为:template typename function<typename>(argulist);template class classname<typename>;显式实例化只需声明,不需要重新定义。编译器根据模板实现实例声明和实例定义。

显示具体化:对于某些特殊类型,可能不适合模板实现,需要重新定义实现,此时可以使用显示具体化(explicite specialization)。显示实例化需重新定义。格式为:template<> typename function<typename>(argu_list){...};template<> class classname<typename>{...};

C++模板之隐式实例化、显示实例化、隐式调用、显示调用和模板特化详解的更多相关文章

  1. vue构造函数(根实例化时和组件实例对象选项)参数:选项详解

    实例选项(即传给构造函数的options):数据,DOM,生命周期钩子函数,资源,组合,其他 数据 data 属性能够响应数据变化,当这些数据改变时,视图会进行重渲染. 访问方式: 1.通过 vm.$ ...

  2. 【并查集】模板 + 【HDU 1213、HDU 1232、POJ 2236、POJ 1703】例题详解

    不想看模板,想直接看题目的请戳下面目录: 目录: HDU 1213 How Many Tables[传送门] HDU 1232 畅通工程 [传送门] POJ 2236 Wireless Network ...

  3. 使用 c++ 模板显示实例化解决模板函数声明与实现分离的问题

    问题背景 开始正文之前,做一些背景铺垫,方便读者了解我的工程需求.我的项目是一个客户端消息分发中心,在连接上消息后台后,后台会不定时的给我推送一些消息,我再将它们转发给本机的其它桌面产品去做显示.后台 ...

  4. Myeclipse Templates详解(一) —— Java模板基础

    目录 Templates简介 MyEclipse自带Templates详解 新建Template 自定义Template 因为自己比较懒,尤其是对敲重复代码比较厌恶,所以经常喜欢用快捷键和模板,Mye ...

  5. C# 转换符的重载 显示、隐式转换

    class Complex { //保存转换的值 Int32 v; public Complex(Int32 x) { v = x; } public Int32 ToInt32() { return ...

  6. Android中Intent的显示和隐式使用

    Android应用程序中组件之间的通信都少不了Intent的使用,Intent负责对应用中一次操作的动作.动作涉及数据.附加数据进行描述,Android则根据此Intent的描述,负责找到对应的组件, ...

  7. Intent 显示意图 隐式意图

    //显式意图  :必须指定要激活的组件的完整包名和类名 (应用程序之间耦合在一起) // 一般激活自己应用的组件的时候 采用显示意图  //隐式意图: 只需要指定要动作和数据就可以 ( 好处应用程序之 ...

  8. 2.8 (显示、隐式、线程休眠) selenium 等待方式 ❀

    http://blog.csdn.net/pf20050904/article/details/20052485 http://www.cnblogs.com/hellokitty1/p/629584 ...

  9. dll显式加载与隐式加载

    使用动态DLL有两种方法,一种是隐式链接,一种是显式链接,如果用loadlibrary就是显示链接,用lib就属于隐式链接. 两种方法对于你的程序调用动态库时没有任何区别,只是你在编程时,步骤是不一样 ...

随机推荐

  1. 阿里巴巴为什么主推HSF?比Dubbo有哪些优势?

    作者:匿名用户链接:https://www.zhihu.com/question/39560697/answer/187538165来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请 ...

  2. jstl的函数

    <%@ taglib prefix="c"  uri="http://java.sun.com/jsp/jstl/core"%><%@ tag ...

  3. APP公布到应用市场(苹果APP STORE+安卓各大应用市场)

    注意事项 1.应用要签名,为了以后可以顺利更新应用.要保持每次的签名一致,所以要妥善保管好签名数据. 2.进行公布測试,最好有个检查表,每次公布的时候进行核查. 苹果APP STORE 一.证书的导出 ...

  4. 解读Unity中的CG编写Shader系列1——初识CG

    CG=C for Graphics  用于计算机图形编程的C语言超集 前提知识点: 1.CG代码必须用 CGPROGRAM ... ENDCG括起来 2.顶点着色器与片段着色器的主函数名称可任意,但须 ...

  5. 小数运算需要注意什么? 接口和抽象类 WinForm窗体上两个panel,怎么实现一个panel固定漂浮在另一个panel之上

    小数运算需要注意什么? 1. 生活中0.1+0.2=0.3, 计算机中可不是这样,为什么呢? 大家都知道计算机类型都是有数据范围的.整形int范围是 正负21亿左右,小数类型同样也是有范围的,但是即使 ...

  6. CentOS Linux搭建独立SVN Server全套流程(转)

    环境为centos6.3 1.首先 看看机器上安装了svn了没有 rpm -qa |grep svn 2.如果没有安装 执行 yum -y install subversion 3.安装好了之后 新建 ...

  7. [框架安装趟雷指南]Ubuntu+1060+cuda+cudnn+Keras+TH+TF+MXnet

    [框架安装趟雷指南]Ubuntu+1060+cuda+cudnn+Keras+TH+TF+MXnet https://zhuanlan.zhihu.com/p/23480983 天清 9 个月前 写这 ...

  8. Python编写的ARP扫描工具

    源码如下: rom scapy.all import * import threading import argparse import logging import re logging.getLo ...

  9. Jenkins 搭建

    持续集成(CI continuous integration) 可以做什么? 自动构建.定时触发,或由某个事件触发.比如可以做 daily build,或每次代码提交时触发.这样可以最早发现代码编译和 ...

  10. adb tcp 调试

    su setprop service.adb.tcp.port 5555 stop adbd start adbd