C++模板之隐式实例化、显示实例化、隐式调用、显示调用和模板特化详解

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

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

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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

程序成功运行并输出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();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

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


2. 显示实例化

2.1模板显示实例化的定义

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

2.2函数模板的显示实例化

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

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

例如:

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

2.3类模板的显示实例化

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

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

例如:

template class theclass<int>;
  • 1
  • 1

3.函数模板调用方式

3.1隐式模板实参调用

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

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

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; //显示调用,通过编译
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

直接采用函数调用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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

程序正常编译运行结果: 
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
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

程序运行结果和使用函数模板特化相同。但是,使用普通函数重载和使用模板特化还是有不同之处,主要表现在如下两个方面: 
(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();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/509471.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

交叉排序

题目描述 输入N个数&#xff0c;把所有奇数位置上的数从小到大排序&#xff0c;把偶数位置上的数从大到小排序。输入 输入的第一行是一个正整数N(2<N<100)。 第二行是N个用空格隔开的整数。输出 输出只有一行N个数&#xff0c;是按要求排序后的序列&#xff0c;用空格隔开…

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

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

IO之 管道流

管道流: 实现两个线程之间的数据交互. PipedInputStream PipedOutputStream PipedReder PipedWriter 中文API解释&#xff1a; PipedInputStream public class PipedInputStreamextends InputStream管道输入流应该连接到管道输出流&#xff1b;管道输入流提供要写入管道输出流的…

AOE网上的关键路径

题目描述 一个无环的有向图称为无环图&#xff08;Directed Acyclic Graph&#xff09;&#xff0c;简称DAG图。 AOE(Activity On Edge)网&#xff1a;顾名思义&#xff0c;用边表示活动的网&#xff0c;当然它也是DAG。与AOV不同&#xff0c;活动都表示在了边上&#xff…

IO之NIO

NIO:New IO: 从JDK1.4开始提出的,新的IO,可以把一块磁盘文件映射到内存中,我们再去读取内存中的数据. 存放在java.nio包中. Java NIO&#xff08;New IO&#xff09;是从Java 1.4版本开始引入的一个新的IO API&#xff0c;可以替代标准的Java IO API 现在主要运用于服务器中…

图结构练习——判断给定图是否存在合法拓扑序列

题目描述 给定一个有向图&#xff0c;判断该有向图是否存在一个合法的拓扑序列。输入 输入包含多组&#xff0c;每组格式如下。 第一行包含两个整数n&#xff0c;m&#xff0c;分别代表该有向图的顶点数和边数。(n<10)后面m行每行两个整数a b&#xff0c;表示从a到b有一条有…

VS2013 加快编译速度 的方法整理

VS2013 加快编译速度 的方法整理 1.更改项目设置 项目|属性|C/C|代码生成|启用最小重新生成&#xff1a;Yes(/Gm)项目|属性|C/C|常规|调试信息格式&#xff1a;程序数据库(/Zi)项目|属性|配置属性-链接器-常规-“启用增量链接”选择“是” 最后&#xff0c;最好还要修改一处&a…

IO流总结

四大基流: InputStream—- OutputStream Reader —- Writer IO流的总结和梳理: 文件流: FileInputStream FileOutputStream FileReader FileWriter 缓冲流: BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter 转换流(把字节转换为字符): …

编程修养

http://blog.csdn.net/haoel/article/category/9200/2

完美网络

题目描述 完美网络是连通网络的基础上要求去掉网络上任意一条线路&#xff0c;网络仍然是连通网络。求一个连通网络要至少增加多少条边可以成为完美网络。输入 第一行输入一个数T代表测试数据个数&#xff08;T<20&#xff09;。每个测试数据第一行2个数n,m 分别代表网络基站…

C++异常处理类与自定义异常处理类

转自&#xff1a;http://blog.csdn.net/makenothing/article/details/43273137 例1&#xff1a;自定义一个继承自excepton的异常类myException C标准中&#xff0c;定义在<stdexcept>中的任何异常类都派生自exception Class&#xff0c;本例也只是简单地由exception继承&…

网络编程概念总结

计算机网路: 计算机网络&#xff0c;是指将地理位置不同的具有独立功能的多台计算机及其外部设备&#xff0c;通过通信线路连接起来&#xff0c;在网络操作系统&#xff0c;网络管理软件及网络通信协议的管理和协调下&#xff0c;实现资源共享和信息传递的计算机系统。 从逻辑…

数据结构实验:连通分量个数

题目描述 在无向图中&#xff0c;如果从顶点vi到顶点vj有路径&#xff0c;则称vi和vj连通。如果图中任意两个顶点之间都连通&#xff0c;则称该图为连通图&#xff0c; 否则&#xff0c;称该图为非连通图&#xff0c;则其中的极大连通子图称为连通分量&#xff0c;这里所谓的极…

如何优化简历

http://mp.weixin.qq.com/s?__bizMzAxMzUzNzYyNA&mid402350492&idx1&sn0e180c05248c845d6ed3e43f5006679e#rd

网络编程之 application/x-www-form-urlencoded MIME编码

编码作用&#xff1a; https://baike.baidu.com/item/魏杰/15581?fraladdin如果我们想要这种表单提交中链接的中文信息不显示出来&#xff0c;那么我们就需要通过MIME编码把中文转换成另外一种编码显示出来,但是这种编码在国内的浏览器中运用的并不多&#xff0c;谷歌浏览器用…

数据结构实验之图论八:欧拉回路

题目描述 在哥尼斯堡的一个公园里&#xff0c;有七座桥将普雷格尔河中两个岛及岛与河岸连接起来。 能否走过这样的七座桥&#xff0c;并且每桥只走一次&#xff1f;瑞士数学家欧拉最终解决了这个问题并由此创立了拓扑学。欧拉通过对七桥问题的研究&#xff0c;不仅圆满地回答…

Linux面试相关

1.TCP/IP 建立连接的过程&#xff1f;&#xff08;3-way shake&#xff09; Client与server建立TCP连接时&#xff1a;首先Client向server发SYN&#xff08;请求&#xff09;&#xff0c;然后server回复&#xff08;应答请求&#xff09;&#xff0c;最后Client回复&#xff0c…

网络编程之 传输层的协议TCP与UDP

传输层协议: TCP和UDP的区别: TCP&#xff1a;面向连接(经历三次握手)、传输可靠(保证数据正确性,保证数据顺序)、用于传输大量数据(流模式)、速度慢&#xff0c;建立连接需要开销较多(时间&#xff0c;系统资源)。 服务端和客户端 UDP&#xff1a;面向非连接、传输不可靠(丢…

n a^o7 !

题目描述 All brave and intelligent fighters, next you will step into a distinctive battleground which is full of sweet and happiness. If you want to win the battle, you must do warm-up according to my instructions, which can make you in the best state prep…

socket阻塞与非阻塞,同步与异步

转自&#xff1a;http://blog.csdn.net/hguisu/article/details/7453390 socket阻塞与非阻塞&#xff0c;同步与异步 作者&#xff1a;huangguisu 1. 概念理解 在进行网络编程时&#xff0c;我们常常见到同步(Sync)/异步(Async)&#xff0c;阻塞(Block)/非阻塞(Unblock)四种调…