解读C++编程中类模板的三种特化

 更新时间:2016年01月08日 17:08:19   作者:MasterFT  
这篇文章主要介绍了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, 就得和主模板的接口相匹配.而如果你是自己再定义的一个显式/局部特化版本类,就不需要匹配 一致.(见显式特化和局部特化),最后指出两点: 首先,除了成员函数外,其实成员也可以被显式特化,如静态成员和成员模板.其次,显式特化是为模板或模板成员提供定制版本的一种手段;而显式实例化仅仅是明确地告诉编译器去实例化一个成员.       

相关文章

  • C语言动态内存泄露常见问题内存分配改进方法详解

    C语言动态内存泄露常见问题内存分配改进方法详解

    今天遇见了一道有意思的内存泄露题目,特地分享给大家,相信屏幕前的你学习完一定有所收获,预祝读者学习愉快,多多进步早日升职加薪
    2021-10-10
  • C++图文并茂讲解继承

    C++图文并茂讲解继承

    继承是C++面向对象编程中的一门。继承是子类继承父类的特征和行为,或者是继承父类得方法,使的子类具有父类得的特性和行为。重写是子类对父类的允许访问的方法实行的过程进行重新编写,返回值和形参都不能改变。就是对原本的父类进行重新编写,但是外部接口不能被重写
    2022-05-05
  • 初识C++ Vector模板与实例化原理

    初识C++ Vector模板与实例化原理

    这篇文章主要为大家介绍了初识C++ Vector模板与实例化原理,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • C++基于人工智能搜索策略解决农夫过河问题示例

    C++基于人工智能搜索策略解决农夫过河问题示例

    这篇文章主要介绍了C++基于人工智能搜索策略解决农夫过河问题,简单描述了农夫过河问题的概念、实现原理并结合具体实例形式给出了C++使用人工智能搜索策略解决农夫过河问题的相关操作技巧,需要的朋友可以参考下
    2017-12-12
  • 基于C语言实现的迷宫游戏代码

    基于C语言实现的迷宫游戏代码

    这篇文章主要介绍了基于C语言实现的迷宫游戏代码,对于学习游戏开发的朋友相信有一定的借鉴价值,需要的朋友可以参考下
    2014-08-08
  • 详解C语言读取文件求某一列的平均值

    详解C语言读取文件求某一列的平均值

    本文粗浅比较了C语言中常用的几种读取文件的函数的效率,并给出了几段求取某列平均值的代码,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多度进步
    2022-02-02
  • c语言double类型默认输出小数几位

    c语言double类型默认输出小数几位

    在本篇文章里小编给大家分享的是关于c语言double类型默认输出小数几位的相关知识点,需要的朋友们可以学习下。
    2020-04-04
  • C++调用python(执行py文件)的全过程

    C++调用python(执行py文件)的全过程

    这篇文章主要给大家介绍了关于C++调用python(执行py文件)的相关资料,文中通过图文以及实例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2021-12-12
  • 浅谈C语言数组元素下标为何从0开始

    浅谈C语言数组元素下标为何从0开始

    很多同学可能在学习数组时会有这个疑问,下标为什么不从1开始呢?本文主要介绍了浅谈C语言数组元素下标为何从0开始,感兴趣的可以了解一下
    2022-01-01
  • C++ primer超详细讲解关联容器

    C++ primer超详细讲解关联容器

    两个主要的关联容器为map和set,map中元素是一些关键字—值对,关键字起索引的作用,值则表示与索引相关联的数据。set中每个元素只包含一个关键字,set支持高效的关键字查询操作——检查一个给定关键字是否在set中
    2022-07-07

最新评论