C++ 请使用特质类(traits classes)表现类型信息

20180404 C++ 请使用特质类(traits classes)表现类型信息


STL主要由“用以表现容器、迭代器和算法”的templates构成,但也覆盖若干工具性模板,其中一个名为advance,用来将某个迭代器移动某个特定距离:
template<typename IterT,typename DistT>  //将迭代器向前移动d单位
void advance(IterT& iter,DistT d);    //若d<0 则向后移动。


观念上advance只是做iter+=d动作,但其实不可以全然那么实践,因为只有随机访问(random access)迭代器才支持+=操作,面对其他威力不那么强大的迭代器种类,advance必须反复执行++或--,共d次。




复习STL的迭代器分类(共5种):
(1)Input迭代器:只能向前移动,一次一步,客户只能读取(不能修改)他们所指的东西,而且只能读取一次,他们模仿指向输入文件的阅读指针(read pointer);C++程序库中的istream_iterators是这一分类的代表;
(2)Output迭代器情况类似,但一切只为输出,另外客户只能修改他们所指的东西,而且只能修改一次,他们模仿指向输出文件的修改指针(write pointer);ostream_itetators是这一分类的代表。


上述两种迭代器威力较小(只能向前移动),所以只适合“一次性操作算法(one-pass algorithms)”。




(3)forward迭代器:这种迭代器可以做前述所有的操作,而且可以读或写所指物一次以上,这使得它们可施行于多次性操作算法。
(4)Bidirectional迭代器:威力更强大,它除了可以向前移动,还可以向后移动,STL的list迭代器就属于这一类,set,multiset,map,和multimap的迭代器也都属于这一类。
(5)随机访问迭代器:威力最大,它可以执行迭代器运算,即它可以在常量时间内向前或向后跳跃任意距离。vector,deque,和strig提供的迭代器就是这一分类。




对于以上5种迭代器,C++标准程序库分别提供专属的卷标结构(tag struct)加以确认:
struct input_iterator_tag{};


struct output_iterator_tag{};


struct forward_iterator_tag:public input_iterator_tag{};


struct bidirectional_iterator_tag:public forward_iterator_tag{};


struct random_access_iterator_tag:public bidirectional_iterator_tag{};


这些structs之间的继承关系是有效的is-a关系,所有的forward迭代器都是input迭代器,依此类推。




我们希望使用如下方式实现advance:


template<typename IterT,typename DistT>
void advance(IterT& iter,DistT d)
{
  if(iter is a random access iterator)
  {
    iter += d;  //针对random access迭代器使用迭代器算数运算
  }
  else
  {
    if(D >= 0){while(d--) ++iter;}  //针对其他迭代器分类反复调用++或--
    else {while(d++) --iter;}
  }
}




这种方法首先必须判断iter是否为随机访问迭代器,traits会帮你办到。




标准技术将类型的traits信息放进一个template及其一个或多个特化版本中,这样的templates在标准程序库中有若干个,其中针对迭代器者被命名为iterator_traits:




template<typename IterT>  //模板,用来处理迭代器分类的相关信息
struct iterator_traits;


iterator_traits的运作方式是,针对每一个类型IterT,在结构体iterator_traits<IterT>里一定声明某个typedef名为iterator_category。这个typedef用来确认Itert的迭代器分类。


以下是对advance实践先前的伪码(pseudocode):
typename<typename IterT,typename DistT>
void advance(IterT& iter,Dist d)
{
  if(typeid(typename std::iterator_traits<IterT>::iterator_category)
     == typeid(std::random_access_iterator_tag))
  ...
}




上述代码会导致编译问题。
我们真正想要的是一个条件式(即if...else语句)判断“编译期核定成功”的类型。恰巧C++有一个取得这种行为的办法,那就是重载。


为了让advance的行为如我们所愿,我们需要做的就是产生两版重载函数,内含advance的本质内容,但各自接受不同类型的iterator_category对象,我们将这两个函数取名为doAdvance:


template<typename IterT,typename DistT>//这份实现用于随机访问迭代器
void doAdvance(IterT& iter,DistT d,std::random_access_iterator_tag)
{
    iter += d; 
}


template<typename IterT,typename DistT>//这份实现用于bidirectional迭代器
void doAdvance(IterT& iter,DistT d,std::bidirectional_iterator_tag)
{
    if(d <= 0){while(d--)  ++iter;}
    else {while(d++) --iter;}
}




template<typename IterT,typename DistT>//这份实现用于input迭代器
void doAdvance(IterT& iter,DistT d,std::input_iterator_tag)
{
  if(d < 0)
  {
    throw std::out_of_range(Negative distance);
  }
  while(d--) ++iter;
}


编译器运用重载解析机制调用适当的实现代码:
template<typename IterT,typename DistT>
void advance(IterT& iter,DistT d)
{
  doAdvance(
    iter,d,
    typename
      std::iterator_traits<IterT>::iterator_category()
    );
}






注意:
1、特性类(Traits class)使得“类型相关信息”在编译期可用,它们以“模板”和
“模板特化”完成实现。
2、整合重载技术(overloading)后,特性类(Traits class)有可能在编译期对类型执行if...else测试。










阅读更多
上一篇java java面向对象-接口的特点和接口的继承
下一篇java java面向对象-接口的实现关系
想对作者说点什么? 我来说一句

Learn the MFC C++ Classes

2009年01月07日 2.93MB 下载

C++运行时类型信息揭密

2012年02月16日 1KB 下载

c++ classes

2014年09月02日 736KB 下载

窗口类分析 WNDCLASSEX

2010年04月17日 222KB 下载

Unity3D中文手册

2012年12月07日 8.16MB 下载

Maven打包,指定classes路径

2013年04月02日 9KB 下载

学生宿舍系统(EJB、JSF技术等)

2009年03月10日 2.17MB 下载

Java类(classes)的学习

2009年06月15日 12KB 下载

没有更多推荐了,返回首页

关闭
关闭