C++——模板特化和偏特化

2023-10-27

1.引言
C++中的模板分为类模板和函数模板,虽然它引进到C++标准中的时间不是很长,但是却得到了广泛的应用,这一点在STL中有着充分的体现。目前,STL在C++社区中得到了广泛的关注、应用和研究。理解和掌握模板是学习、应用和研究以及扩充STL的基础。而STL模板实例中又充斥着大量的模板特化和偏特化。


2.模板的定义
(1) 类模板
定义一个栈的类模板,它可以用来容纳不同的数据类型
说明如下:
template <class T>
class stack {
private:
  list* top;
public:
  stack();
  stack(const stack&);
  ~stack();
  void push(T&);
  T& pop();
  //…
};
上述定义中,template告诉编译器这是一个模板,尖括号中的<class T >指明模板的参数,可以有一个或多个,具体实现时由用户指定,其中template <class T >中的关键字class可以用关键字typename来代替。
类模板的使用除了要在声明时指明模板参数外,其余均与普通的类相同,例如:
stack<int> int_stack;
stack<char> ch_stack;
stack<string> str_stack;
int_stack.push(10);
ch_stack.push(‘z’);
str_stack.push(“c++”);
(2)函数模板
假设现在要定义一个max函数来返回同一类型(这种类型是允许比较的)两个值的最大者.
template<class T>
T mymax(const T& t1,const T& t2)
{ return t1 < t2 ? t2 : t1; }
template <class T>的意义与类模板定义中相同。
模板函数的使用与普通非模板函数使用相同,因为模板函数的参数可以从其传入参数中解析出来。例如:
int highest = mymax(5,10);
char c = mymax(‘a’, ’z’);
mymax(5,10)解析出模板函数参数为int, mymax(‘a’, ’z’)解析出模板函数的参数为char。


3.模板的特化
(1)类模板特化
有时为了需要,针对特定的类型,需要对模板进行特化,也就是特殊处理.例如,stack类模板针对bool类型,因为实际上bool类型只需要一个二进制位,就可以对其进行存储,使用一个字或者一个字节都是浪费存储空间的.
template <class T>
class stack {};
template < >
class stack<bool> { //…// };
上述定义中template < >告诉编译器这是一个特化的模板。
(2) 函数模板的特化
看下面的例子
main()
{
  int highest = mymax(5,10);
  char c = mymax(‘a’, ’z’);
  const char* p1 = “hello”;
  const char* p2 = “world”;
  const char* p = mymax(p1,p2);
}
前面两个mymax都能返回正确的结果.而第三个却不能,因为,此时mymax直接比较两个指针p1 和 p2 而不是其指向的内容.
针对这种情况,当mymax函数的参数类型为const char* 时,需要特化。
template <class T>
T mymax(const T t1, const T t2)
{
   return t1 < t2 ? t2 : t1;
}

template <>
const char* mymax(const char* t1,const char* t2)
{
   return (strcmp(t1,t2) < 0) ? t2 : t1;
}
现在mymax(p1,p2)能够返回正确的结果了。


4.模板的偏特化
模板的偏特化是指需要根据模板的某些但不是全部的参数进行特化
(1) 类模板的偏特化
例如c++标准库中的类vector的定义
template <class T, class Allocator>
class vector { // … // };
template <class Allocator>
class vector<bool, Allocator> { //…//};
这个偏特化的例子中,一个参数被绑定到bool类型,而另一个参数仍未绑定需要由用户指定。
(2) 函数模板的偏特化
  严格的来说,函数模板并不支持偏特化,但由于可以对函数进行重载,所以可以达到类似于类模板偏特化的效果。
  template <class T> void f(T);  (a)
  根据重载规则,对(a)进行重载
  template < class T> void f(T*);  (b)
  如果将(a)称为基模板,那么(b)称为对基模板(a)的重载,而非对(a)的偏特化。C++的标准委员会仍在对下一个版本中是否允许函数模板的偏特化进行讨论。


5.模板特化时的匹配规则
(1) 类模板的匹配规则
最优化的优于次特化的,即模板参数最精确匹配的具有最高的优先权
例子:
template <class T> class vector{//…//}; // (a)  普通型
template <class T> class vector<T*>{//…//};  // (b) 对指针类型特化
template <>   class vector <void*>{//…//};  // (c) 对void*进行特化
每个类型都可以用作普通型(a)的参数,但只有指针类型才能用作(b)的参数,而只有void*才能作为(c)的参数
(2) 函数模板的匹配规则
非模板函数具有最高的优先权。如果不存在匹配的非模板函数的话,那么最匹配的和最特化的函数具有高优先权
例子:
template <class T> void f(T);  // (d)
template <class T> void f(int, T, double); // (e)
template <class T> void f(T*);  // (f)
template <> void f<int> (int) ; // (g)
void f(double);  // (h)
bool b;
int i;
double d;
f(b); // 以 T = bool 调用 (d)
f(i,42,d) // 以 T = int 调用(e)
f(&i) ; // 以 T = int* 调用(f)
f(d);  //  调用(g)我怎么觉得是调用(h)
参考文献
[1] Bjarne Stroustrup, The C++ Programming Language (Special Edition), Addison Wesley,2000
[2] Nicolai M.Josuttis, The C++ Standard Library – A Tutorial and Reference ,Addison Wesley,1999
[3] Stanley Lippman and Josée Lajoie ,C++ Primier, 3rd Edition ,Addison Wesley Longman ,1998

 

转载自:http://blog.csdn.net/zhang810413/archive/2007/12/18/1948603.aspx

 

补充:

 

Partial Template Specialization能够让你在模板(Template)的所有可能的实体中特化出一组子集.
1.模板的特化(template specialization):
    例如,定义如下的一个模板:
    template<class Window, class Controller>
    class Widget
    {
      ... 泛化实现代码 ...
    };
    然后你可以像下面那样明确地加以特化:
    template<>    //注意:
template后面的尖括号中不带任何内容;
    class Widget<ModalDialog, MyController>
    {
      ... 特化实现代码 ...
    };
    其中ModalDialog和MyController是你自己另外定义的类;有了这个Widget的特化定义之后,如果你以后定义了Widget<ModalDialog, MyController>对象时,编译器就会使用上述的特化定义,如果定义了其它泛型对象,那么编译器就是用原本的泛化定义;这就是模板的特化.

2.Partial Template Specialization(模板偏特化)
    模板特化是通过"给模板中的所有模板参数一个具体的类"的方式来实现的.而模板偏特化则是通过"给模板中的部分模板参数以具体的类,而留下剩余的模板参数仍然使用原来的泛化定义"的方式来实现的;
    比如,就上面的Widget类模板的定义来说,有时候想针对任意的Window来搭配一个特定的MyController类特化Widget,这个时候就需要使用模板偏特化机制了.下面的Widget类模板就是Widget的偏特化定义:
    template<class Window>                        //仍然使用原来的泛化定义;
    class Widget<Window, MyController>            //MyController是具体的类,是特化定义;
    {
      ... 偏特化实现代码 ...
    };
    这就是一个偏特化定义;一个MyController类可以搭配任意一种Window.
    通常在一个类模板的偏特化定义中,你只会特化某些模板参数而留下其它泛化参数.当你在程序中具体实现上述类模板的时,编译器会试着找出最匹配的模板定义.这个寻找过程十分复杂精细,允许你以富有创意的方式来进行偏特化.例如,假设你有一个Button类模板,它有一个模板参数,那么,你不但可以拿任意的Window搭配特定的MyController来特化Widget,还可以拿任意Button搭配特定的MyController来偏特化Widget:
    template<class ButtonArg>
    class Widget<Button<ButtonArg>, MyController>    //使用任意Button搭配具体的类MyContorller
    {
      ... 偏特化实现代码 ...
    };
    模板的偏特化能力很强大.当你实例化一个模板时,编译器会把目前存在的偏特化模板和全特化模板做比较,并找出其中最合适、最匹配的实现.这样,灵活性就很大.但是不幸的是,模板的偏特化机制不能用在函数身上,不论成员函数还是非成员函数.

注意:
1.虽然你可以全特化类模板中的成员函数,但是你不能偏特化他们;
2.你不能偏特化命名空间级别(namespace-level)的函数(non-member).最接近"命名空间级别模板函数"的偏特化机制就是函数重载,那就意味着你对"函数参数"(而非返回值类型或内部所用类型)有很精致的特化能力;
3.特化或全特化时,template后面的尖括号中不带任何内容;

总结:
模板特化/全特化是指给每一个模板参数一个具体的类型,以具体实现这个模板,而且template后面的尖括号中不带任何内容;
模板偏特化是指只给部分模板参数一个具体的类型,来实现这个模板;

 

转载自:http://hi.baidu.com/klcdyx2008/blog/item/5adbf77b79f316f90bd1873c.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++——模板特化和偏特化 的相关文章

  • 创建 DirectoryEntry 实例以供测试使用

    我正在尝试创建 DirectoryEntry 的实例 以便可以使用它来测试将传递 DirectoryEntry 的一些代码 然而 尽管进行了很多尝试 我还是找不到实例化 DE 并初始化它的 PropertyCollection 的方法 我有
  • 如何在没有 Control.Invoke() 的情况下从后台线程修改控件属性

    最近 我们遇到了一些旧版 WinForms 应用程序 我们需要更新一些新功能 在专家测试该应用程序时 发现一些旧功能被破坏 无效的跨线程操作 现在 在您认为我是新手之前 我确实有一些 Windows 窗体应用程序的经验 我不是专家 但我认为
  • 跨多个控件共享事件处理程序

    在我用 C 编写的 Windows 窗体应用程序中 我有一堆按钮 当用户的鼠标悬停在按钮上时 我希望按钮的边框发生变化 目前我有以下多个实例 每个按钮一个副本 private void btnStopServer MouseEnter ob
  • C# 中可空类型是什么?

    当我们必须使用nullable输入 C net 任何人都可以举例说明 可空类型 何时使用可空类型 https web archive org web http broadcast oreilly com 2010 11 understand
  • 使用 C# 在 WinRT 中获取可用磁盘空间

    DllImport kernel32 dll SetLastError true static extern bool GetDiskFreeSpaceEx string lpDirectoryName out ulong lpFreeBy
  • 更改android中禁用按钮的颜色

    有没有办法通过样式或其他形式更改 android 中禁用按钮的颜色 我目前有以下内容 可绘制 button default xml
  • 当 Cortex-M3 出现硬故障时如何保留堆栈跟踪?

    使用以下设置 基于 Cortex M3 的 C gcc arm 交叉工具链 https launchpad net gcc arm embedded 使用 C 和 C FreeRtos 7 5 3 日食月神 Segger Jlink 与 J
  • 基于范围的 for 循环中的未命名循环变量?

    有没有什么方法可以不在基于范围的 for 循环中 使用 循环变量 同时也避免编译器发出有关未使用它的警告 对于上下文 我正在尝试执行以下操作 我启用了 将警告视为错误 并且我不想进行像通过在某处毫无意义地提及变量来强制 使用 变量这样的黑客
  • 按字典顺序对整数数组进行排序 C++

    我想按字典顺序对一个大整数数组 例如 100 万个元素 进行排序 Example input 100 21 22 99 1 927 sorted 1 100 21 22 927 99 我用最简单的方法做到了 将所有数字转换为字符串 非常昂贵
  • .Net Core / 控制台应用程序 / 配置 / XML

    我第一次尝试使用新的 ConfigurationBuilder 和选项模式进入 Net Core 库 这里有很多很好的例子 https docs asp net en latest fundamentals configuration ht
  • 编译的表达式树会泄漏吗?

    根据我的理解 JIT 代码在程序运行时永远不会从内存中释放 这是否意味着重复调用 Compile 表达式树上会泄漏内存吗 这意味着仅在静态构造函数中编译表达式树或以其他方式缓存它们 这可能不那么简单 正确的 他们可能是GCed Lambda
  • 如何在 Team Foundation 上强制发表有意义的签入评论?

    我有一个开发团队有一个坏习惯 他们写道poor签入评论 当我们必须在团队基础上查看文件的历史记录时 这使得它成为一场噩梦 我已经启用了变更集评论政策 这样他们甚至可以在签到时留下评论 否则他们不会 我们就团队的工作质量进行了一些讨论 他们很
  • 更改窗口的内容 (WPF)

    我创建了一个简单的 WPF 应用程序 它有两个 Windows 用户在第一个窗口中填写一些信息 然后单击 确定 这会将他们带到第二个窗口 这工作正常 但我试图将两个窗口合并到一个窗口中 这样只是内容发生了变化 我设法找到了这个更改窗口内容时
  • 用 C 实现 Unix shell:检查文件是否可执行

    我正在努力用 C 语言实现 Unix shell 目前正在处理相对路径的问题 特别是在输入命令时 现在 我每次都必须输入可执行文件的完整路径 而我宁愿简单地输入 ls 或 cat 我已经设法获取 PATH 环境变量 我的想法是在 字符处拆分
  • AccessViolationException 未处理

    我正在尝试使用史蒂夫 桑德森的博客文章 http blog stevensanderson com 2010 01 28 editing a variable length list aspnet mvc 2 style 为了在我的 ASP
  • 如何在内存中存储分子?

    我想将分子存储在内存中 这些可以是简单的分子 Methane CH4 C H bond length 108 7 pm H H angle 109 degrees But also more complex molecules like p
  • ListDictionary 类是否有通用替代方案?

    我正在查看一些示例代码 其中他们使用了ListDictionary对象来存储少量数据 大约 5 10 个对象左右 但这个数字可能会随着时间的推移而改变 我使用此类的唯一问题是 与我所做的其他所有事情不同 它不是通用的 这意味着 如果我在这里
  • 在Linux中使用C/C++获取机器序列号和CPU ID

    在Linux系统中如何获取机器序列号和CPU ID 示例代码受到高度赞赏 Here http lxr linux no linux v2 6 39 arch x86 include asm processor h L173Linux 内核似
  • 方法参数内的变量赋值

    我刚刚发现 通过发现错误 你可以这样做 string s 3 int i int TryParse s hello out i returns false 使用赋值的返回值是否合法 Obviously i is but is this th
  • 如何将字符串“07:35”(HH:MM) 转换为 TimeSpan

    我想知道是否有办法将 24 小时时间格式的字符串转换为 TimeSpan 现在我有一种 旧时尚风格 string stringTime 07 35 string values stringTime Split TimeSpan ts new

随机推荐

  • 深度学习总结(一)各种优化算法

    参考博文 码农王小呆 https blog csdn net manong wxd article details 78735439 深度学习最全优化方法总结 https blog csdn net u012759136 article d
  • 矢量vector之间用等号赋值的问题

    结论 可以 在程序上编个小程序试试就能知道了 vector v1 v1 pushback 2 v1 pushback 3 v1 pushback 4 vector v2 v1 cout lt lt v2 0 lt lt v2 1 lt lt
  • StringUtils 工具类 详细介绍

    https blog csdn net laukicn article details 69230022
  • Java 程序员,真的不能去外包吗?

    Java程序员是可以去外包的 外包公司通常会为客户提供技术服务 包括程序开发 系统维护和支持等 作为Java程序员 如果你有较强的Java技能 那么可以考虑去外包公司工作 在外包公司工作的优势包括 有机会去不同的客户处工作 能够更多地接触不
  • shell 脚本day4之 sed应用

    应用sed编写的点名器 root zabbix server day4 more name txt 李白 杜甫 白居易 孟浩然 苏轼 root zabbix server day4 more roll sh bin bash 功能描述 De
  • 用js实现滚动加载动画效果

    目录 一 效果图 二 代码部分 1 html结构 2 css样式部分 3 js部分 三 代码总结 一 效果图 可以看出 在悠方滚动条滚动的时候 当高度打到一定高度的时候就会出现一个div盒子 就好像刚加载出来一样 而且可以一直向下滚动 二
  • github 中使用 ssh

    从去年开始 github 对于 https 方式下载的仓库已经不支持直接 commit push 等操作 由于 https 方式简单快捷 对于大部分只是对 git 初步熟悉的用户来说是非常合适的 因为不需要做任何配置 只需要一个 githu
  • js之DOM0级和DOM2级绑定事件

    js原生注册事件分别为DOM0级 DOM2级 DOM0级事件 简单理解就是直接绑定 可以是直接在标签中绑定事件
  • LVGL笔记8--lv_style样式

    LVGL笔记8 lv style样式 样式是用来修饰UI美观性 使用lv style对UI界面进行重绘和重用 利用多个不同的样式来形成Theme对象 对于vl obj基础对象而言 每个对象都有一个lv style样式 但对于其他的控件 比如
  • 基于Java+SpringBoot+vue的社区报修维修平台(含源码和数据库)

    文章目录 简介 环境需要 住户前台功能模块 管理员功能模块 住户后台功能模块 维修员后台功能模块 简介 系统管理也都将通过计算机进行整体智能化操作 对于社区维修平台所牵扯的管理及数据保存都是非常多的 例如住户管理 社区公告管理 维修工管理
  • 目标检测笔记(十三): 使用YOLOv5-7.0版本对图像进行目标检测完整版(从自定义数据集到测试验证的完整流程))

    文章目录 一 目标检测介绍 二 YOLOv5介绍 2 1 和以往版本的区别 三 代码获取 3 1 视频代码介绍 四 环境搭建 五 数据集准备 5 1 数据集转换 5 2 数据集验证 六 模型训练 七 模型验证 八 模型测试 九 评价指标 一
  • Jupyter Notebook 添加代码自动补全功能

    自己记录以便后期参考与查询 转载参考 https www jianshu com p 0ab80f63af8a 安装 如果之前安装过显示目录功能的话 这一步骤可以跳过 pip install jupyter contrib nbextens
  • 【测试 2】二、软件质量与软件测试过程

    2 软件质量 2020年10月29日01 16 39 质量三要素 实体 特性 需求 软件质量6大特性 27个子特性 参考 https www cnblogs com jodyccf p 12200325 html 软件质量活动 保证软件质量
  • 认识RAID磁盘阵列

    认识RAID磁盘阵列 前几天在公司整理办公桌时找到了一份关于RAID的文档 对RAID介绍的很详细 这几天我利用空闲时间把这份文档录入到了电脑里 现分享给大家 认识RAID磁盘阵列 廉价冗磁盘阵列 Redundant Array of In
  • 运放使用总结篇(1) 运算放大器基本概念简介

    文章目录 前言 一 运算放大器是什么 二 运放的开环增益 三 运放的输入阻抗 输出阻抗 四 运算放大器的基本结构 五 运放的开环和闭环使用 总结 前言 作为硬件设计中最常用的运算放大器 有必要了解和掌握 单个三极管放大倍数离散度很大 虽然硬
  • IOS object-c基础

    第一讲 OC简介及基本语法 Objective C简称OC是在C语言的基础上 增加了一层最小的面向对象语法 完全兼容C语言 也就是可以在OC代码中混入C语言代码 甚至是C 代码 可以使用OC开发Mac OS X平台和IOS平台的应用程序 简
  • CAD动态块制作

    CAD动态块制作 拉伸动态块 柜体A拉伸动态块制作 第一步 制作柜体A 第二步 进入块编辑器编辑 第三步 关闭块编辑器 柜体B拉伸动态块制作 第一步 制作柜体B 第二步 进入块编辑器 第三步 关闭块编辑器 可见性动态块 第一步 找到动态块图
  • 计算机视觉基础之数字图像(2)

    目录 一 直方图 1 什么是直方图 2 直方图的性质 3 直方图均衡化 4 相关示例代码 二 滤波 1 定义 2 线性滤波 三 卷积 1 定义 2 卷积 过滤器 卷积核 Kernel 3 卷积的应用 4 卷积 步长 5 卷积 填充 一 直方
  • Web服务器如何确定哪个Servlet处理请求

    一 Web服务器 Web服务器概念较为广泛 我们最常说的Web服务器指的是网站服务器 它是建立在Internet之上并且驻留在某种计算机上的程序 Web服务器可以向Web客户端 如浏览器 提供文档或其他服务 只要是遵循HTTP协议而设计的网
  • C++——模板特化和偏特化

    1 引言C 中的模板分为类模板和函数模板 虽然它引进到C 标准中的时间不是很长 但是却得到了广泛的应用 这一点在STL中有着充分的体现 目前 STL在C 社区中得到了广泛的关注 应用和研究 理解和掌握模板是学习 应用和研究以及扩充STL的基