1、
所谓模板特例,是针对符合某种条件的模板参数值集合另外声明的模板实现变体。
template<typename T> class my_vector;
template<> class my_vector<bool>;
 
2、特例的多种写法
  1. template<typename T, int i> struct S1;
  2.  
  3. template<typename T, int i, template<typename, int> class SP>
  4. struct S;//这里先定义一个模板通例
  5.  
  6. template<int i, template<typename, int> class SP>
  7. struct S<char, i, SP>;//省略了typename T
  8.  
  9. template<typename T, int i, template<typename, int> class SP>
  10. struct S<const T, i, SP>;//约束第一个模板参数的类型必须用const修饰
  11.  
  12. template<>
  13. struct S<char, , S1>;//完全特例,其匹配式中没有用到任何模板参数,所有项目都是确定的类型或值或模板。故其模板参数列表为空。相应的,我们将模板参数列表不为空的特例成为部分特例
  14.  
  15. template<typename T>
  16. struct S<S1<T, >, , S1>;//难点理解:同特例2一样,这里也是对T的一种约束,它要求S这个模板的第一个参数必须是S1<T, 10>模板的实例,而第三个参数要是S1型的某个模板,这里不要搞混(搞混的话就回头去看模板型模板参数)
3、特例的匹配规则
  1. template<typename T0, typename T1, typename T2>
  2. struct S
  3. {
  4. std::string id()
  5. {
  6. return "General";
  7. }
  8. };
  9.  
  10. template<typename T0, typename T1>
  11. struct S<T0, T1, char>
  12. {
  13. std::string id()
  14. {
  15. return "Specialization #1";
  16. }
  17. };
  18.  
  19. template<typename T0>
  20. struct S<T0, char, char>
  21. {
  22. std::string id()
  23. {
  24. return "Specialization #2";
  25. }
  26. };
  27.  
  28. template<typename T>
  29. struct S<int, T, T>
  30. {
  31. std::string id()
  32. {
  33. return "Specialization #3";
  34. }
  35. };
  36.  
  37. void main()
  38. {
  39. using namespace std;
  40. printf("%s\n", S<float, float, float>().id().c_str());//匹配通例
  41. printf("%s\n", S<int, int, int>().id().c_str());//只与特例3匹配
  42. printf("%s\n", S<int, int, char>().id().c_str());//与特例1匹配,因为特例3要求后两个类型是相同的
  43. printf("%s\n", S<char, char, char>().id().c_str());//与特例1和特例2均匹配,但特例2比特例1更加特殊
  44. printf("%s\n", S<int, char, char>().id().c_str());//在特例2和特例3之间无法判断谁更特殊
  45.  
  46. getchar();
  47. }
4、函数模板的特例与重载
函数模板特例的写法与类模板特例的写法十分相似。
函数重载是要匹配函数的参数,而函数模板特例则是要匹配模板参数。粗看二者似乎并不想干,但C++中海油一个函数模板参数推导机制。当没有显式给定模板实参值时,编译器会尝试由函数调用的实参类型推导出模板参数值。有此推导机制,函数模板参数值在某些情况下也可与函数调用的实参相关。
特例与重载这两种既想干又不完全等价的机制并存,使得处理函数模板变得异常复杂且潜藏众多冲突。为化简问题,C++标准中只允许为函数模板声明完全特例,而禁止为其声明部分特例。大多数需要用到部分 特例的情况都可以利用函数模板重载来实现。
 
区分模板特例与重载:
准则一:两候选函数中如果有一方其形参列表个类型与调用实参列表各类型更匹配,则淘汰另一方。
准则二:两函数如果其形参列表类型同等匹配实参列表类型时,若一方为函数模板实例而另一方为非模板函数,则去非模板函数而淘汰函数模板实例。
准则三:两函数如果其形参列表类型同等匹配实参列表类型时,若两者均为函数模板实例,则去更为特殊的一方淘汰另一方。
  1. template<typename T>
  2. void func(T v)
  3. {
  4. printf("#1\n");
  5. }
  6.  
  7. template<>
  8. void func(float v)
  9. {
  10. printf("#2\n");
  11. }
  12.  
  13. void func(float v)
  14. {
  15. printf("#3\n");
  16. }
  17.  
  18. void main()
  19. {
  20. func();//只有#1才有可能是int,其它全是float
  21. func(.);//1.是double类型,只有#1才有可能是double,其它全是float
  22. func(.f);//#1和#2和#3都与实参类型完全匹配。#2比#1更加特殊,淘汰掉#1。#3与#2之间比较选择非模板函数#3。
  23. func<>(.f);//这里显式指定要使用的是一个模板实例
  24.  
  25. getchar();
  26. }

编译期递归逻辑:
#2与#3接口完全一致。也就是说,你可以完全不用#3这个完全模板特例,而只使用#2这样的重载替代模板完全特例就好了。因为,前面的#2中,func(float v)可以通过实参的float推导出模板中的T是float,所以使用#3func重载的效果和使用完全模板特例是一样的,没必要再设完全模板特例了,(也就是说通过实际使用参数float v已经推断出了T,但是如果实参和模板参数没有联系呢?)那么模板完全特例存在的意义为何?
比如说,有模板参数不与函数参数类型相关,此时的模板特例不能用函数重载替代。例如,模板有个非模板型模板参数,则显然不能通过调用实参类型推导,而需要在模板实参列表中给定明确的值。
举例,
template<int i>
void print(float f)
{
//这里边使用i
}
 
这个模板的模板参数与函数实参没有任何关系,且唯一的模板参数是非模板型模板参数。那么根据 i 的取值不同,其模板实例的意义不同。你给print<1>~print(100)都设为特例则有可能有100个不同的意义,所以如果有一个重载函数void print(float f),你知道这个print()要在何时执行吗哪一个吗?
print<1>(float);//完全特例
print<100>(float);//完全特例
假如有一个,print(float)你不知道这里的值到底是1-100实例中的哪一个,这里就不能用函数重载替代完全模板特例。
现在,print(1.)就完全不能代替模板了。
 
实例如下:
  1. template<int i>
  2. void print()
  3. {
  4. print<i - >();
  5. printf("%d\n", i);
  6. }
  7.  
  8. template<>
  9. void print<>()
  10. {
  11. printf("1!\n");
  12. }
  13.  
  14. void main()
  15. {
  16. print<>();
  17. getchar();
  18. }
 
 
 
 

《深入实践C++模板编程》之四——特例的更多相关文章

  1. 《深入实践C++模板编程》之五——容器与迭代器

    1.容器的定义 容器:专门用于某种形式组织及存储数据的类称为“容器”.   2.容器与迭代器 迭代器:封装了对容器虚拟数据序列的操作并按约定提供统一界面以遍历容器内容的代理类即为迭代器.   举例理解 ...

  2. 《深入实践C++模板编程》之六——标准库中的容器

    1.容器的基本要求 a.并非所有的数据都可以放进容器当中.各种容器模板对所存数据类型都有一个基本要求——可复制构造.将数据放进容器的过程就是通过数据的复制构造函数在容器内创建数据的一个副本的过程. b ...

  3. 《深入实践C++模板编程》之三——模板参数类型详解

    非类型模板参数 和 模板型模板参数 整数以及枚举类型:指向对象或者函数的指针:对对象或函数的引用:指向对象成员的指针.统称为非类型模板参数. 模板型模板参数,是指模板参数还可以是一个模板.   1.整 ...

  4. 《深入实践C++模板编程》之二——模板类

    1.类的模板的使用 类,由于没有参数,所以没有模板实参推导机制. #include <stdexcept> template<typename T> class my_stac ...

  5. 《深入实践C++模板编程》之一——Hello模板

    1.通过一个简单的例子来理解模板的用途: 模板为不同类型的数据生成操作相同或相似的函数. 弱语言如Python,可以使用一种函数来应对各种类型,但是C++就不得不为不同的类型编写相似的函数.模板的作用 ...

  6. C++之模板编程

    当我们越来越多的使用C++的特性, 将越来越多的问题和事物抽象成对象时, 我们不难发现:很多对象都具有共性. 比如 数值可以增加.减少:字符串也可以增加减少. 它们的动作是相似的, 只是对象的类型不同 ...

  7. 深入C++04:模板编程

    模板编程 函数模板 模板意义:对类型也进行参数化: 函数模板:是不编译的,因为类型不知道 模板的实例化:函数调用点进行实例化,生成模板函数 模板函数:这才是要被编译器所编译的 函数模板.模板的特例化. ...

  8. c++模板编程-typename与class关键字的区别

    最近一直在研究c++模板编程,虽然有些困难,但希望能够坚持下去.今天,在书上看见一个讨论模板编程typename与class两个关键字的区别,觉得挺有意义的,就把它们给总结一下. 先看一个例子: te ...

  9. [推荐]ORACLE PL/SQL编程之四:把游标说透(不怕做不到,只怕想不到)

    原文:[推荐]ORACLE PL/SQL编程之四:把游标说透(不怕做不到,只怕想不到) [推荐]ORACLE PL/SQL编程之四: 把游标说透(不怕做不到,只怕想不到) 继上两篇:ORACLE PL ...

随机推荐

  1. Linux如何修改环境变量

    如果想要环境变量永久生效可以修改下面两个文件中的任何一个: 1 /etc/profile 2 .bash_profile 其中,/etc/profile是全局的环境变量,对所有用户生效,而.bash_ ...

  2. ElasticSearch2:集群安装

    0.Linux系统参数设置 Linux进程数系统限制查看 [root@ip101 config]# sysctl kernel.pid_max kernel.pid_max = 131072 [roo ...

  3. window 定时任务小项目,每秒钟,每分钟访问url

    个人百度网盘中 链接: https://pan.baidu.com/s/15sK2H-OtrAkAYcXjsxwihA 提取码: awv3 复制这段内容后打开百度网盘手机App,操作更方便哦

  4. QT程序拷贝 转移 改变运行环境

    qt程序 在windows平台下怎么运行? 以前开发环境是VS2008编译 +qt-win-opensource-4.7.4-vs2008框架 +QtCreator编辑界面(以前的例子,win7下成功 ...

  5. sqlserver创建链接服务器连接sqlserver脚本

    示例: EXEC sp_addlinkedserver @server='MyLinkServer', --链接服务器别名 @srvproduct='', @provider='SQLOLEDB', ...

  6. ubuntu下如何关闭某个端口?

    1. 开启防火墙 sudo ufw enable 2. 关闭某个端口,如80端口 sudo ufw deny 80 3. 查询当前防火墙状态 sudo ufw status

  7. <JavaScript>数组的sort()方法中比较函数是怎么工作的

    sort()函数比较时调用的是每个数组项的toString()方法,并非按数值大小进行比较,所以往往得不到我们想要的结果. 比如: ,,,,]; values.sort( ); alert(value ...

  8. LC 265. Paint House II

    There are a row of n houses, each house can be painted with one of the k colors. The cost of paintin ...

  9. 根据需求定制 admin

    定义 list 页面 自定义 list_filter 首先,完成过滤器的功能,需要自定义过滤器.在 PostAdmin 定义的上方定义如下代码: class CategoryOwnerFilter(a ...

  10. C++代码匈牙利命名规范

    一.类 除了异常类等个别情况(不希望用户把该类看作一个普通的.正常的类之情况)外,C++类/结构的命名应该遵循以下准则: C++类的命名    类的名称都要以大写字母“C”开头,后跟一个或多个单词.为 ...