traits技法小计

在学习算法导论的时候,对于各数据结构,自然是实现一个才算掌握,工具当然是template编程,但是自己的demo经常存在很多问题,比如没有给出迭代器啊,操作符重载不够啊等等设计上的问题,而某些问题实际上是从设计之初就该考虑的大框架,而非小细节。对于C++而言,STL无疑是最佳的参考资料,侯捷先生的STL源码剖析一书给我们良好的示范,而直接从第四章开始看会云里雾里,无法得其精髓,因此在学习算法之余决定尾随侯捷先生脚步,学习STL traits技法,从而可以从STL中学到更多的数据结构实现。

收获自是颇多,不仅可以掌握数据结构的基础知识,还会教会我们如何去筛选那些实现的方案,比如hash_table选择了chaining法解决冲突,map和set采用红黑树等等,而且对我们C++泛型编程技法和C++重载技术也是极好的锻炼,源码之前,尽是宝藏。当然前提是有良好的数据结构知识和C++primer程度的C++技能。本文对侯捷先生的第二章进行简单概括,便于日后查看,将一些和traits不太相关的比如C++语法等略去。

我们以list的迭代器开发来逐步走入STL 迭代器的世界
  1 template<typename T>
  2 class List      //链表
  3 {
  4     void insert_front(T value);
  5     void insert_end(T value);
  6     void display(ostream &os=cout) const;
  7 private:
  8     ListItem<T>* _end;
  9     ListItem<T>* _front;
 10     long _size;
 11 };
 12
 13 template<typename T>
 14 class ListItem          //结点
 15 {
 16 public:
 17     T get_value()const;
 18     ListItem* next()const;
 19 private:
 20     T _value;
 21     ListItem* _next;
 22 };
 23
 24 //要定义迭代器需要封装指针
 25 template<class Item>  // Item的实际参数是ListItem
 26 struct ListIter
 27 {
 28     Item *ptr;     //指向结点的指针,迭代器就是在此基础上封装
 29
 30     ListIter(Item *p=0):ptr(p){}//构造函数
 31
 32     Item& operator*()const{return *ptr;}//解引用
 33     Item* operator->()const{return ptr;}//->操作符
 34     //...
 35 };
 36
 37 /*
 38 这里存在问题,我们将listiter封装,并于list绑定,用于泛型算法
 39 时,无法获取iter所指对象型别以及迭代器距离,所指之物的引用和指针等,
 40 STL中借助模板类型推导来实现这一点
 41 */
 42 //第一想法是在模板形参表当中加入另一个参数,即所指之物的类型,然后将双参数的函数放在private中,在public接口函数中调用private函数
 43
 44 template<typename Item, typename value>
 45 class Item
 46 {
 47 private:
 48     void func_pri(Item iter,value v);
 49 public:
 50     void func(Item iter){func_pri(iter,*iter);}
 51 };
 52 //问题又来了,如果在返回值中想要获取类型怎么办?C++ 的参数推导不可以在返回值中使用,因为从C继承而来的函数的必要因素是参数和函数名,调用
 53 //一个函数并不需要返回值,因此若支持返回值自动推导,编译器可能无法获知其类型
 54
 55 这里还有一种方法完成:
 56 template<class value>
 57 class Iter                    //    在迭代器的定义中内嵌一个关于value_type的声明
 58 {
 59     typedef value value_type;
 60 };
 61
 62 template<class I>
 63 typename Iter::value_type  func(I i)   //这是某个Iter适用的函数(泛型算法,并不是Iter类的成员函数),算法的形参为迭代器类型,在其中可以访问value_type,符合我们的预期
 64 {
 65     return *i;
 66 }
 67
 68 //只有一个小问题,和指针不兼容,因为指针不可内嵌声明,因为它不是类类型。
 69 我们想到了C++中template编程的一种技法,即部分特化,注意部分特化和特化是不同的,特化后实质已经不是模板,而部分特化是对模板的一个特殊情况,仍是模板
 70 接下来正式介绍traits技法
 71
 72 -------------------------------------------------------------------------------------------------
 73 //trait技法
 74 template<class I>
 75 struct iterator_traits
 76 {
 77     typedef typename I::value_type value_type;
 78     typedef typename I::iterator_category iterator_category;
 79     /*此型别比较特殊,是因为iterator有5种(写入、只读、读写、双向、随机),应用于泛型算法时,不同算法针对不同类型的迭代器有不同的效率,应该分开实现
 80     比如,对于+操作而言,随机迭代器效率是O(1),写入是O(n),应该针对随机迭代器另行实现
 81     让我们想到了函数重载,但问题是上面的不同类型的迭代器都是模板参数,编译器无法获取它的类型,因此无法重载,因此加入这一个参数作为标志,此标志必须是类类型,才可以推导进行重载决议
 82     具体的实现见P95*/
 83     typedef typename I::difference_type difference_type;
 84     typedef typename I::pointer pointer;
 85     typedef typename I::reference reference;
 86 };
 87 部分特化版本--原生指针
 88 template<class P>
 89 struct iterator_traits<P*>
 90 {
 91     typedef typename P value_type;
 92     typedef typename random_access_iterator_tag iterator_category;
 93     typedef typename ptrdiff_t difference_type;
 94     typedef typename P* pointer;
 95     typedef typename P& reference;
 96 };
 97
 98 部分特化版本--const指针
 99 template<class P>
100 struct iterator_traits<const P*>
101 {
102     typedef typename P value_type;
103     typedef typename random_access_iterator_tag iterator_category;
104     typedef typename ptrdiff_t difference_type;
105     typedef typename const P* pointer;
106     typedef typename const P& reference;
107 };
108 ------------------------------------------------------------------------------------------------------------------
109
110 template<class I>
111 typename iterator_traits<I>::value_type  func(I i)  //这个和上面的无区别,就是间接取了一下,但好处在于有部分特化版本,对于不同类型的参数会返回正确的类型
112 {
113     return *i;
114 }
115
116
117 我们自己实现iterator的时候,可以继承iterator class,见P100
 

traits技法小计,布布扣,bubuko.com

时间: 2024-10-12 16:00:08

traits技法小计的相关文章

汽车仪表是如何计算总计里程和小计里程的?

现在汽车仪表大部分的总计里程和小计里程都是显示在屏幕上的,这包括段码屏.点阵屏.TFT彩屏等,虽然显示形式不一样,但是从业务需求和软件应用层的实现策略来讲,原理应该都是通用的.本文不涉及具体车型,仅对一般的业务逻辑作介绍,一是为了自己总结记录,二是期望吸引同行或爱好者交流. 1.总计里程 ODO(Total Odometer )即总计里程,顾名思义,主要作用是记录汽车总的行驶里程,一般来讲,在用户使用过程中是无法对其修改或清零的,因为它是对二手汽车价值评估的一项重要数值,当然随意篡改这一数据也是

设计模式小计——23种设计模式3

责任链模式Chain of Responsibility 使多个对象都有机会处理请求,从而避免请求的发送者和接受者间的耦合关系,并沿着这条链传递请求,直到有对象处理它为止 责任链模式关键是建立链接关系,在链中决定谁来处理请求 //抽象处理者 public abstract class Handle{ private Handle nextHandle;//链的下一个节点 public final Response handleMessage(Request request){ Response

设计模式小计——23种设计模式1

单例模式Singleton Pattern 确保类只有一个实例,而且自行实例化并向整个系统提供这个实例 public class Singleton{ private static final Singleton singleton = new Singleton(); private Singleton(){} public static Singleton getSingleton(){ return singleton; } } 节省内存开支,减少性能开销,应用启动产生单例对象,永久驻留内

设计模式小计——23种设计模式2

模板方法模式Template Method Pattern 定义一个操作的算法的框架,是的子类可以不改变算法结构即可重定义该算法一些特定步骤 public abstract class AbstractClass{//抽象模板类 protected abstract void method1();//算法步骤1 protected abstract void method2();//算法步骤2 public void templateMethod(){//模板方法,定义算法结构 this.met

带你深入理解STL之迭代器和Traits技法

在开始讲迭代器之前,先列举几个例子,由浅入深的来理解一下为什么要设计迭代器. //对于int类的求和函数 int sum(int *a , int n) { int sum = 0 ; for (int i = 0 ; i < n ; i++) { sum += *a++; } return sum; } //对于listNode类的求和函数 struct ListNode { int val; ListNode * next; }; int sum(ListNode * head) { int

Sql使用WITH ROLLUP 进行分类汇总及小计功能

效果如下: 测试代码如下: CREATE TABLE #test    (      Name VARCHAR(10) ,      [procedure] CHAR(1) ,      model VARCHAR(5) ,      quantity INT    ); INSERT  INTO #test        SELECT  'A' ,                '1' ,                'φ50' ,                500        UNI

RandomStrngUtils工具类小计

//产生5位长度的随机字符串,中文环境下是乱码 RandomStringUtils.random(5); //使用指定的字符生成5位长度的随机字符串 RandomStringUtils.random(5, new char[]{'a','b','c','d','e','f', '1', '2', '3'}); //生成指定长度的字母和数字的随机组合字符串 RandomStringUtils.randomAlphanumeric(5); //生成随机数字字符串 RandomStringUtils.

c++11小计

[capture] (parameters) mutable -> return-type { statement } " (parameters)" 和 "-> return-type"在不需要的情况下可省略. mutable:mutable修饰符.默认情况下,lambda函数总是一个const函数,mutable可以取消常量性.在使用该修饰符时,参数列表不可省略(即使为空). 在使用lambda函数的时候,如果需要捕捉的值成为lambda函数的常量,

小计使用多线程和gevent来提高celery性能及稳定性

前言: 有朋友问我,我那个任务队列是怎么实现,他的疑问其实主要是celery不支持多线程.先说说我那实现的方法,其实我的做法和celery.rq这样的框架很像的,都是把任务push到队列里面,然后pull取出任务而已,celery里面还可以取任务,我这个是通过传送uuid来实现的.   朋友问celery不支持多线程,那是他没有好好看文档.celery是支持多任务并发的,哎... 好好看文档呀. 原文:http://rfyiamcool.blog.51cto.com/1030776/153082