1、
所谓模板特例,是针对符合某种条件的模板参数值集合另外声明的模板实现变体。
template<typename T> class my_vector;
template<> class my_vector<bool>;
 
2、特例的多种写法
template<typename T, int i> struct S1;

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

编译期递归逻辑:
#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.)就完全不能代替模板了。
 
实例如下:
template<int i>
void print()
{
print<i - >();
printf("%d\n", i);
} template<>
void print<>()
{
printf("1!\n");
} void main()
{
print<>();
getchar();
}
 
 
 
 

《深入实践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. php反序列化笔记

    普通的魔法方法 public,private,protected属性序列化后的不同 绕过wakeup session反序列化 phar反序列化 1.普通的魔法方法 __construct() 创建一个 ...

  2. mysql5.7 彻底解决sql_mode=only_full_group_by

    ONLY_FULL_GROUP_BY是mysql默认的一种sql模式,其作用是约束sql语句:要求select中的所有字段,除复合函数外,全部要出现在group by中. 默认这种模式是有原因的,因为 ...

  3. Flume-安装与 NetCat UDP Source 监控端口

    Flume 文档:https://flume.apache.org/FlumeUserGuide.html Flume 下载:https://archive.apache.org/dist/flume ...

  4. leetcode84 柱状图

    O(n^2) time 应用heights[r]<=heights[r+1]剪枝: class Solution { public: int largestRectangleArea(vecto ...

  5. 一百二十二:CMS系统之页面抽离和登录页面

    将登录和注册需要的共性标签抽离出来做父模板 将css改名为base base模板 {% from 'common/_macros.html' import static %}<!DOCTYPE ...

  6. intellij import包 顺序调整

    intellij中自动import的包顺序与eclipse不太一致,可以参照以下方式进行调整: eclipse中(笔者用的是eclipse luna)导入包的顺序依次是: javajavaxorgco ...

  7. 1、vinc = vict 胜、征服

  8. Linux服务器集群性能监控之Performance Co-Pilot(PCP)部署

    转载自:https://blog.csdn.net/w84268426/article/details/78431778 在部署PCP时,我用到了两台cent os 7虚拟机. 1.官方安装文档htt ...

  9. 树莓派实现摄像头监控(使用motion和mjpg-streamer)

    购买raspBerryCarmen,大概20元, 启动树莓派,安装: `sudo apt install motion` 配置/etc/motion/motion.conf, `sudo vim /e ...

  10. 安装最新docker-ce失败解决

    报错 下载 检查本地是否已经安装 rpm -qa |grep containerd.io 如果有低版本的,卸载即可. 安装新版的containerd.io软件包 wget https://downlo ...