解读C++编程中类模板的三种特化
1.类模板显式特化
为了进行特化,首先需要一个通用的版本,称主模板.主模板使用了标准库堆算法. 堆 是一种线性化的树形结构,将一个值压入一个堆中, 实际上等于将该值插入到一个树形结构中;将一个值从堆中取出就等于移除并返回堆中最大值.但在处理字符的指针时会碰钉子.堆将按照指针的值进行组织. 我们可以提供一个显式特化版本解决此问题(例1)如果希望除了一个针对const char*的Heap外,还希望提供一个针对char *的Heap;(例2)
//主模板 template <typename T> class Heap { private: std::vector<T> h_; public: void push(const T& val); T pop(); bool empty() const //const声明在末尾表示该函数不能修改类变量 { return h_.empty(); } } template <typename T> void Heap<T>::push(const T& val) { h_.push_back(val); std::push_heap(h_.begin(),h_.end()); } template <typename T> T Head<T>::pop() { std::pop_head(h_.begin(),h_.end()); T tmp(h_.back()); h_.pop_back(); return tmp; }
例1
//显示特化版本 /*********************************************** * 可以看到模板参数列表是空的,其实这根本不是一个模 * 板. 因为没有指定任何模板参数.所以模板的显式特化又被 * 称作"完全特化". * Heap<const char*> 完全特化,不会导致模板的实例化; * Heap<int> 特化,会导致模板的实例化; * 编译器根据主模板的声明来检查类模板特化. ***********************************************/ template<>//注意,无任何参数,当然,它本来就不是一个模板 class Head<const char *> { private: std::vector<const char *> h_; public: void push(const char *pval); const char * pop(); bool empty() const //const声明在末尾表示该函数不能修改类变量 { return h_.empty(); } }; //再次提醒, Head<const char *>不是一个模板 void Heap<const char*>::push(const char *pval) { h_.push_back(pval); std::push_heap(h_.begin(),h_.end()); }
例2
/*********************************************** * C++没有要求显式特化的接口必须和主模板的接口完全 * 匹配.如该例中,没有定义主模板的empty函数,并且自行增加 * 了size和capitalize两个函数. * 提醒:此例中不定义empty函数是不可取的,定义模板的 * 显式特化和类的派生之间虽然不存在任何技术上的联系,但 * 是用户依然可以参考类的派生的优点,让特化版本至少具有 * 主模板的基本能力. ***********************************************/ template<>//注意,无任何参数,当然,它本来就不是一个模板 class Head<char *> { private: std::vector<char *> h_; public: void push(char *pval); char * pop(); //注意,此处没有提供empty函数哟!!! size_t size() const; void capitalize(); };
2.模板局部特化
模板局部特化首先要声明的是,C++还不支持对函数模板的局部特化,所以此处我们只讨论类模板的局部特化.我们依然首先需要一个主模板.(参考类模板显式特化) 自我理解:如果针对不能的指针定义不同的完全特化,岂不是太麻烦了,有没有更好的办法呢?那就是局部特化了.(例1)提示: 局部特化它是一个模板.完全特化不是一样模板.
例1
/*********************************************** * 局部特化 * 和完全特化不同,这里的Heap参数类型只是被部分的确 * 定为T*,而T是一个未指定的类型,这就是为什么说它是局部 * 特化的原因; * 当使用一个未经任何修饰的指针类型来实例化Heap时, * 局部特化将优先于主模板; * 当使用const char * 或 char *(参考类模板显式特化)来 * 实例化Heap时,此时完全特化又会优先于局部特化. * Heap<std::string> h1; 主模板 T是std::string * Heap<std::string *> h2; 局部特化 T是std:string * Heap<int **> h3; 局部特化 T是int * * Heap<char *> h4; 完全特化 T是char * * Heap<const int *> h5; 局部特化 T是const int * Heap<int (*)()> h6; 局部特化 T是int() ***********************************************/ template <typename T> class Heap<T *> //注意这里 { private: std::vector<T *>h_; public: void push(const T *val); T *pop(); bool empty() { return h_.empty(); } }; template <typename T> void Heap<T *>::push(const T *val) { //...... }
例2
/*********************************************** * 有一点很微妙但很有用:主模板的完全特化或局部特化 * 必须采用与主模板相同数量和类型的实参进行实例化,但它 * 的模板的参数并不需要具有和主模板相同的形式. ***********************************************/ //定义一个模板,有三个模板参数,书写形式如下 template <typename R,typename A1,typename A2> //注意,局部特化中,模板参数也是三个,但书写形式可不一样喽 class Heap<R (*) (A1,A2)> { //...... }; Heap<char *(*) (int,int)> h7; //R是char *,A1和A2是int //把 char *(*) (int,int) 想象成一个"指向有两个参数的非成员函数的指针" template <class C,typename T> class Heap<T C::*> { //...... }; Heap<std::string Name::*> h8;//T是string,C是Name
尽管为何需要对这些东西使用Heap只是一个猜测,先知道有这么一用法吧!
3.类模板成员特化
虽然模板的特化和类的派生之间没有任何关系, 但在特化模板的时候,不妨借鉴一下派生的精神.也就意味着一个完全特化或局部特化通常必须重新实现 主模板具备的所有能力.
例:
//主模板 template <typename T> class Heap { private: std::vector<T> h_; public: void push(const T& val); T pop(); bool empty() const //const声明在末尾表示该函数不能修改类变量 { return h_.empty(); } } //其实我们真正需要特化的是 push 和 pop两个函数. //对比显式特化,它是通过主模板,再写一个模板显式特化版本类; //而这里只是对类模板成员进行了单独特化. template<> void Heap<const char*>::push(const char *const &pval) { h_.push_back(pval); std::push_heap(h_.begin(),h_.end(),strLess); } template<> const char* Heap<const char*>::pop() { std:pop_heap(h_.begin(),h_end(),strLess); const char* tmp = h_.back(); h_.pop_back(); return tmp; }
注意,这些函数的接口必须和 "它们正在特化其成员" 的模板的相应接口相匹配.如例1, 就得和主模板的接口相匹配.而如果你是自己再定义的一个显式/局部特化版本类,就不需要匹配 一致.(见显式特化和局部特化),最后指出两点: 首先,除了成员函数外,其实成员也可以被显式特化,如静态成员和成员模板.其次,显式特化是为模板或模板成员提供定制版本的一种手段;而显式实例化仅仅是明确地告诉编译器去实例化一个成员.
相关文章
- vector是表示可以改变大小的数组的序列容器,本文主要介绍了C++STL标准库std::vector的使用详解,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2022-03-06
- 这篇文章主要介绍了C++中取余运算的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-23
- 这篇文章主要介绍了C++ string常用截取字符串方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25
- 本文通过例子,讲述了C++调用C#的DLL程序的方法,作出了以下总结,下面就让我们一起来学习吧。...2020-06-25
- 本篇文章主要介绍了C++中四种加密算法之AES源代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。...2020-04-25
- 整数拆分,指把一个整数分解成若干个整数的和。本文重点给大家介绍C++ 整数拆分方法详解,非常不错,感兴趣的朋友一起学习吧...2020-04-25
- 这篇文章主要介绍了C++中Sort函数详细解析,sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变...2022-08-18
- 这篇文章主要介绍了C++万能库头文件在vs中的安装步骤(图文),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-23
- 这篇文章主要介绍了C++ bitset用法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25
- 本篇文章小编并不是为大家讲解string类型的用法,而是讲解我个人比较好奇的问题,就是string 类型占几个字节...2020-04-25
- 这篇文章主要为大家详细介绍了C++ Eigen库计算矩阵特征值及特征向量,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-04-25
- 这篇文章主要介绍了C++ pair的用法实例详解的相关资料,需要的朋友可以参考下...2020-04-25
- 这篇文章主要介绍了VSCode C++多文件编译的简单使用方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-03-29
- 虽然C++11引入了智能指针的,但是开发人员在与内存的斗争问题上并没有解放,如果我门实用不当仍然有内存泄漏问题,其中智能指针的循环引用缺陷是最大的问题。下面通过实例代码给大家介绍c++中的循环引用,一起看看吧...2020-04-25
- 这篇文章主要给大家介绍了关于C++随机点名生成器的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25
- map容器是C++ STL中的重要一员,删除map容器中value为指定元素的问题是我们经常与遇到的一个问题,下面这篇文章主要给大家介绍了关于利用C++如何删除map容器中指定值的元素的相关资料,需要的朋友可以参考借鉴,下面来一起看看吧。...2020-04-25
- 这篇文章主要介绍了C++ 约瑟夫环问题案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下...2021-08-15
- 这篇文章主要介绍了C++中cin的用法详细,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25
- 本篇文章是对C++中的常见编译错误进行了详细的分析介绍,需要的朋友参考下...2020-04-25
- 在本篇内容里小编给大家分享了关于C++实现递归函数的教学步骤,需要的朋友跟着参考下。...2020-04-25