《c++ templates》学习笔记(9)——第十二章 特化与重载
1 第十二章 特化与重载
1.1 重载函数模板
和一般的函数重载类似,函数模板也可以进行重载,比如下面的两个f,这是两个同名函数,1和2并没有关系,2不是1的局部特化。2是1的一个重载。
//1
template<typename T1, typename T2>
void g(T1 a, T2 b){
std::cout<<"void g(T1 a, T2 b)"<<std::endl;
}
//2
template<typename T>
void g(T a, int b){
std::cout<<"void g(T a, int b)"<<std::endl;
};
1.2 显式特化
1.2.1 全局的函数模板特化
其实以前我没有听说过函数模板也能特化,我一直以为函数模板只能重载。
对于函数模板来说,下面的几个定义可以位于同一个翻译单元:
template<typename T1, typename T2>
void g(T1 a, T2 b){
std::cout<<"void g(T1 a, T2 b)"<<std::endl;
}
template<typename T>
void g(T a, int b){
std::cout<<"void g(T a, int b)"<<std::endl;
};
template<>
void g(int a, int b){
std::cout<<"template<> void g(int a, int b)"<<std::endl;
};
void g(int a, int b){
std::cout<<"void g(int a, int b)"<<std::endl;
}
对于编译器来说,排序规则是这样的:局部特化比一般模板特殊;全特化比局部特化特殊;非模板函数比全特化特殊。
全局函数模板特化与类模板特化的区别在于:函数模板引入了重载和实参演绎两个概念。
有了实参演绎,我们就可以用它来确定模板的特殊化版本的实参。这样在函数的全局特化中,我们就可以不用显式声明函数的模板参数。
比如上面的:
template<>
void g(int a, int b){ //1
std::cout<<"template<> void g(int a, int b)"<<std::endl;
};
他其实等同于下面的代码:
template<>
void g<int,int>(int a, int b){ //2
std::cout<<"template<> void g(int a, int b)"<<std::endl;
};
这点可以参考VS2005的MSDN:ms-help://MS.VSCC.v80/MS.MSDN.v80/MS.VisualStudio.v80.chs/dv_vclang/html/eb0fcb73-eaed-42a1-9b83-14b055a34bf8.htm
但是有一点很奇怪,既然上面的两个是等同的,如果我把他们放在同一个头文件中来定义并不会报错。
还有,在以上两个定义都存在的情况下,如果我这样调用:
g<int,int>(1,1);
那么调用的实际上是2,而如果我使用g(1,1)则调用的实际上是2。
更进一步:
如果将2删除,然后再次调用g<int,int>(1,1),那么调用的实际上是:void g(T1 a, T2 b) ,从上面的这些分析我们可以看出:
template<>
void g(int a, int b)
实际上没有被看作是模板。他的优先级介于模板特化和重载的一般函数之间。
上面这个例子的原因是这样的,让我们来整理一下思路,整理之后的代码如下,我们定义了下面4个模板,将那个普通函数的重载删除:
//1
template<typename T1, typename T2>
void g(T1 a, T2 b){
std::cout<<"void g(T1 a, T2 b)"<<std::endl;
}
//这是另外一个模板,他并不是1的局部特化
//因为特化要求模板的参数个数相同
//1有两个模板参数T1, T2
//这里只有一个模板参数
//这属于1的一个函数模板重载
//2
template<typename T>
void g(T a, int b){
std::cout<<"void g(T a, int b)"<<std::endl;
};
//3, 这个其实是的特化
template<>
void g(int a, int b){
std::cout<<"template<> void g(int a, int b)"<<std::endl;
};
//4,这个其实是的特化
template<>
void g<int,int>(int a, int b){
std::cout<<"template<> void g(int a, int b)"<<std::endl;
};
所以:
int _tmain(int argc, _TCHAR* argv[])
{
double d = 1;
g('a', d); //等同于g<char,double>('a',d);
g<char>('a',1); //等同于g<char,int>('a',1);
g<int,int>(1,1);
g(1,1); //等同于g<int>(1,1);
return 0;
}
1.2.2 全局的类模板特化
来看一个特化的例子:
template<typename T>
class Types{
public:
typedef int I;
};
template<typename T ,typename U = typename Types<T>::I>
class S; //(1)
template<>
class S<void>{ //(2)
};
template<>
class S<char, char>;//(3)
int _tmain(int argc, _TCHAR* argv[])
{
S<int>* pi;
S<void>* pv;
S<void, int> sv; //使用2
S<void, char> sc;//
return 0;
}
其中语句S<void, char> sc;会出错: error C2079: 'sc' uses undefined class 'S<T,U>'。
我原来以为该语句会使用S<void>特化,但是看来并不是。所以,怀疑上文的S<void>定义就相当于下面的定义:
template<>
class S<void, int>{};
于是在正文中加上上面这段代码,重新编译,果然报错:error C2766: explicit specialization; 'S<void>' has already been defined 。
对于特化的声明而言,因为它并不是模板声明,所以应该使用普通成员定义语法来定义全局类模板特化的成员。
template<>
class S<void>{ //(2)
public:
void f();
};
//此处没有template<>
void S<void>::f()
{}
注意到其中的f函数的定义。
可以用全局模板特化来代替对应泛型模板的某个实例化体。然而,全局模板特化和由模板特化生成的实例化版本是不能够共同存在于一个程序中的。例如:
template<typename T>
class Invalid{};
Invalid<int> x1;
template<>
class Invalid<int>;
那么编译器会报如下错误: error C2908: explicit specialization; 'Invalid<T>' has already been instantiated。
遗憾的是,如果他们在不同的翻译单元出现,则编译器就发现不了这种错误。我们来看一个例子:
//Chapter12.h
template<typename T>
class Danger{
public:
enum { max = 10};;
};
char buffer[Danger<void>::max];
void clear(char* buf);
//chapter12.cpp
template<typename T>
class Danger;
template<>
class Danger<void>{
public:
enum {max = 100};
};
void clear(char* buf)
{
for(int k = 0; k < Danger<void>::max; ++k)
buf[k] = '"0';
}
//main函数
#include “chapter12.h”
int _tmain(int argc, _TCHAR* argv[])
{
clear(buffer);
return 0;
}
那么,在clear执行的时候,max的值其实是100。这就可能会导致内存访问错误。
在使用特化的时候,我们需要特别小心,并且确认特化的声明对于泛型模板的所有用户都是可见的。这就意味着:在模板声明所在头文件中,特化的声明通常应该位于模板声明的后面。
《c++ templates》学习笔记(9)——第十二章 特化与重载相关推荐
- 系统架构师学习笔记_第十二章_连载
第十二章 系统安全架构设计 12.1 信息系统安全架构的简单描述 信息安全的特征 是为了保证信息的 机密性.完整性.可用性.可控性.不可抵赖性. 以风险策略为基础. 12.1.1 信息安全的现状 ...
- OpenCV学习笔记(五十一)——imge stitching图像拼接stitching OpenCV学习笔记(五十二)——号外:OpenCV 2.4.1 又出来了。。。。。 OpenCV学习笔记(五
OpenCV学习笔记(五十一)--imge stitching图像拼接stitching stitching是OpenCV2.4.0一个新模块,功能是实现图像拼接,所有的相关函数都被封装在Stitch ...
- OpenCV学习笔记(四十一)——再看基础数据结构core OpenCV学习笔记(四十二)——Mat数据操作之普通青年、文艺青年、暴力青年 OpenCV学习笔记(四十三)——存取像素值操作汇总co
OpenCV学习笔记(四十一)--再看基础数据结构core 记得我在OpenCV学习笔记(四)--新版本的数据结构core里面讲过新版本的数据结构了,可是我再看这部分的时候,我发现我当时实在是看得太马 ...
- OpenCV学习笔记(三十一)——让demo在他人电脑跑起来 OpenCV学习笔记(三十二)——制作静态库的demo,没有dll也能hold住 OpenCV学习笔记(三十三)——用haar特征训练自己
OpenCV学习笔记(三十一)--让demo在他人电脑跑起来 这一节的内容感觉比较土鳖.这从来就是一个老生常谈的问题.学MFC的时候就知道这个事情了,那时候记得老师强调多次,如果写的demo想在人家那 ...
- Slicer学习笔记(六十二)slicer下导出模块接口
Slicer学习笔记(六十二)slicer下导出模块接口 1. 参考文件实现 1. 参考文件实现 通过配置config_file为每一个生成类添加 Export,并为每个Module生成 Export ...
- Slicer学习笔记(四十二)slicer c++源码编译
Slicer学习笔记(四十二)slicer c++源码编译 1.cmake生成项目 2.编译失败的原因汇总 2.1.下载代码失败 之前在windows下编译slicer,没有做笔记. 后面再次编译还会 ...
- 李弘毅机器学习笔记:第十二章—Recipe of Deep Learning
李弘毅机器学习笔记:第十二章-Recipe of Deep Learning 神经网络的表现 如何改进神经网络? 新的激活函数 梯度消失 怎么样去解决梯度消失? Adaptive Learning R ...
- Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十五章:第一人称摄像机和动态索引...
Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十五章:第一人称摄像机和动态索引 原文:Introduction to 3 ...
- Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十九章:法线贴图
Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十九章:法线贴图 原文:Introduction to 3D Game P ...
最新文章
- 兵团教师计算机水平考试免考条件,兵团职称计算机考试政策.doc
- Linux进程及进程管理命令
- ubuntu 发行版升级注意事项
- python基础知识8——常见内置模块
- 设计模式--门面(Facade)模式
- MATLAB在通信系统仿真中的注意
- c++ 字符串数组长度排序_数组 | 后缀数组的求法及应用
- linux 条件变量函数,Linux线程同步之条件变量
- 什么linux系统是中文的,什么是Linux系统
- OpenCV中VideoCapture判断isOpened()时总是返回false
- ns.ajax,UIWebView使用NSURLProtocol(拦截),ajax加载失败的问题
- Java 线程池实际运用案例
- 手撸一个基于Springboot+Vue的书籍论坛系统,可用于课程设计和毕业设计或者练手
- 纬衡多个用户荣获“第五届建筑创作奖”
- linux重装系统保留文件,linux重装系统,如何保存硬盘中的内容
- Android 谷歌地图不显示,Android谷歌地图不显示在模拟器
- Oracle基本介绍与基本使用
- python Pygame的具体使用讲解
- 《途客圈创业记:不疯魔,不成活》一一2.8 正式上线
- rust 局域网联机_腐蚀怎么进行局域网联机方式 酷跑加速器和你畅玩游戏世界
热门文章
- Visual Studio Code 配置 Markdown
- cnblog写博客还不错嘛
- vs2005 Key not valid for use in specified state
- delphi SAP
- SpringMVC入门(二)—— 参数的传递、Controller方法返回值、json数据交互、异常处理、图片上传、拦截器
- 使用Navicat连接MySQL时出现2059报错的解决方法
- Python中获取异常(try Exception)信息
- Python requests 多线程抓取 出现HTTPConnectionPool Max retires exceeded异常
- druid抛出的异常------javax.management.InstanceAlreadyExistsException引发的一系列探索
- Ubuntu更新源问题终于解决了