STL源码剖析——iterators与trait编程#2 Traits编程技法
在算法中运用迭代器时,很可能用到其相应类型。什么是相应类型?迭代器所指对象的类型便是其中一个。我曾有一个错误的理解,那就是认为相应类型就是迭代器所指对象的类型,其实不然,相应类型是一个大的类别,迭代器所指对象的类型只是里面的其中一个。后面会讨论到相应类型的另外几种。
假设算法需要声明一个变量,以“迭代器所指对象的类型”为类型,该怎么做?或许我们可能会想到RTTI性质中的typeid(),但获得的只是类型名称,并不能拿来声明变量。
其中一个解决方法是:利用模版函数中的参数推导(argument deducation)机制:
template <typename I, typename T>
void func_impl(I iter, T t)
{
T tmp;
//...做原本func()应该做的全部工作
} template<typename I>
inline void func(I iter)
{
func_impl(iter, *iter);
}
我们以func()为对外接口,却把实际操作全部置于func_impl()之中。由于func_impl()是一个模版函数,一旦被调用,编译器会自动进行参数推导。于是导出类型T,解决了问题。
然而上面也有提到,迭代器的相应类型不只是迭代器所指对象的类型一种而已。根据经验,最常用的相应类型有5种,然而并非任何情况下任何一种都可以利用上述的参数推导机制来取得。我们需要更全面的解法。
Traits编程技法
迭代器所指对象的类型,称为该迭代器的value type。上述的参数类型推导虽然可以用于value type,但并不是什么情况都能用,例如需要把value type当作函数的返回值,这个办法就行不通了,毕竟模版函数的参数推导机制只能推导参数,无法推导函数的返回值类型。
我们需要其他方法,为类型T定义一个别名似乎是一个好主意,例如:
template<typename I>
inline void func(I iter)
{
func_impl(iter, *iter);
} template<typename T>
struct MyIter
{
typedef T value_type;
T* ptr;
MyIter(T* p = ) :ptr(p){}
T& operator*()const { return *ptr; }
//...
}; template<typename T>
typename I::value_type func(I ite)
{
return *ite;
} MyIter<int> ite(new int());
cout << func(ite); //输出 8
T是一个模板参数,在它被编译器具现化之前,编译器对T一无所知,换句话说,编译器此时并不知道MyIter<T>::value_type代表的是一个类型或是一个成员函数或是一个数据成员变量。关键词typename的用意在于告诉编译器这是一个类型,如此才能顺利通过编译。
这似乎可行,但有一点需要注意,并不是所有的迭代器都是类的对象,例如原生指针,如果不是类对象,就无法为它定义别名。但STL绝对必须接受原生指针作为一种迭代器,所以上面这样还不够。那么有没有办法可以让上述的一般化操作针对特定情况做特殊化处理呢?
模版偏特化可以做到,如果模板类拥有一个以上的模版参数,我们可以针对其中的某个或数个模版参数进行特化。换句话说,我们可以在泛化设计中提供一个特化版本,指明某些模板参数。假设有一个模版类如下:
template<typename T>
class C
{...};
偏特化的字面意思容易误导我们,让我们以为所谓的偏特化版本是对参数V指定某个参数值。其实不然,所谓的partial specialization的意思是提供另一份模板定义式,而其本身仍为模版。由此,面对上述这个模板类,我们可以提供另外一份定义:
template<typename T>
class C<T*> //这个特化版本仅适用于T为原生指针的情况
{...};
至此,对于value_type而言,我们有了比较好的提取类型的解决方法。那么Traits编程也已呼之欲出了,何为traits,我的理解就是通过一定的手段把某个类的特征提取出来,在侯捷老师的书中就叫萃取类的特性,何为类的特征或特性?这里可以分为两个类别去阐述,一是迭代器类,二是普通类,两个类别对应的特征不同,本节主要讲迭代器类的特征(也就是前面所述的相应类型,value_type就是其中一种),之后的章节再讲普通类的特征及其提取。而迭代器的相应类型最常用到的有五种,分别是:value type, difference type, pointer, reference, iterator catagoly。
迭代器相应类型之一:value type
所谓value type,是指迭代器所指对象的类型。任何一个打算与STL算法有完美搭配的类,都应该定义自己的value type,做法如上所述。
迭代器相应类型之二:difference type
用来表示两个迭代器之间的距离,因此可以用它来表示一个容器的最大容量,因为对于连续空间的容器而言,头尾之间的距离就是其最大容量。其实我有点不太理解,为什么要为两个迭代器之间的距离刻意定义一个别名,例如STL的count(),其传回值必须使用迭代器的difference type:
template<typename I, typename T>
typename itertor_traits<I>::difference_type //函数返回值,是个类型
count(I first, I last, const T& value) {
typename itertor_traits<I>::difference_type n = ;
for ( ; first != last; ++first)
{
if (*first == value)
++n;
}
return n;
}
实际上定义一个整型变量来记录也是可以的,所以我不太明白difference type的用意。
于原生指针而言,也有类似意义的类型ptrdiff_t(定义于<cstddef>头文件),所以在特化版本中,就把ptrdiff_t作为原生指针的difference_ type:
template<typename I>
struct iterator_traits
{
...
typedef typename I::difference_type difference_type;
};
//针对原生指针而设计的偏特化版本
template<typename T>
struct iterator_traits<T*>
{
...
typedef ptrdiff_t difference_type;
};
//针对原生的pointer-to-const而设计的偏特化版本
struct iterator_traits<const T*>
{
...
typedef ptrdiff_t difference_type;
};
迭代器相应类型之三:reference type
我的理解是,reference type就是表示该类型变量的引用表示,例如要是想返回某迭代器所指类型变量的引用(需要用作左值时),用reference type就好了。侯捷书中并未对reference type作过多的解释,而是把注意力放在了T&和const T&上,即constant iterators(不允许改变所指对象的内容)和mutable iterators(允许改变所指对象的内容)。当p是个mutable iterator时,其value type是T,那么reference type就应该是T&;如果p是一个constant iterators,其value type是T,那么reference type就应该是const T&。实现的细节将在下节给出iterator源码时讨论。
迭代器相应类型之四:pointer type
我的理解是,pointer type就是表示该类型变量的地址表示,也就是说,我们能够传回一个pointer,指向迭代器所指之物。这些相应类型已在先前的ListIter class中出现过:
Item& operator*()const { return *ptr; }
Item* operator->()const { return ptr; }
Item&便是ListIter的reference type,而Item* 便是其pointer type。现在我们可以把reference type和pointer type这两个相应类型加入traits内:
template<typename I>
struct iterator_traits
{
...
typedef typename I::pointer pointer;
typedef typename I::reference reference;
};
template<typename T>
struct iterator_traits<T*>
{
...
typedef T* pointer;
typedef T& reference;
};
struct iterator_traits<const T*>
{
...
typedef const T* pointer;
typedef const T& reference;
};
而由于迭代器相应类型之五占据的篇幅过多,而本节讲的东西已经有点多了,所以把它放在下节再进行学习。
STL源码剖析——iterators与trait编程#2 Traits编程技法的更多相关文章
- STL源码剖析——iterators与trait编程#4 iterator源码
在前两节介绍了迭代器的五个相应类型,并讲述如何利用traits机制提取迭代器的类型,但始终是把iteartor_traits类分割开来讨论,这影响我们的理解,本节将给出iteator的部分源码,里面涵 ...
- STL源码剖析——iterators与trait编程#3 iterator_category
最后一个迭代器的相应类型就是iterator_category,就是迭代器本身的类型,根据移动特性与实行的操作,迭代器被分为了五类: Input Iterator:这种迭代器所指的对象,不允许外界改变 ...
- STL源码剖析——iterators与trait编程#1 尝试设计一个迭代器
STL的中心思想在于:将数据容器与算法分开,独立设计,再用一帖粘着剂将它们撮合在一起.而扮演粘着剂这个角色的就是迭代器.容器和算法泛型化,从技术角度来看并不困难,C++的模板类和模板函数可分别达成目标 ...
- STL源码剖析 迭代器(iterator)概念与编程技法(三)
1 STL迭代器原理 1.1 迭代器(iterator)是一中检查容器内元素并遍历元素的数据类型,STL设计的精髓在于,把容器(Containers)和算法(Algorithms)分开,而迭代器(i ...
- STL源码剖析——Iterators与Traits编程#5 __type_traits
上节给出了iterator_traits以及用到traits机制的部分函数的完整代码,可以看到traits机制能够提取迭代器的特性从而调用不同的函数,实现效率的最大化.显然这么好的机制不应该仅局限于在 ...
- 《STL源码剖析》学习之traits编程
侯捷老师在<STL源码剖析>中说到:了解traits编程技术,就像获得“芝麻开门”的口诀一样,从此得以一窥STL源码的奥秘.如此一说,其重要性就不言而喻了. 之前已经介绍过迭代器 ...
- 【STL 源码剖析】浅谈 STL 迭代器与 traits 编程技法
大家好,我是小贺. 点赞再看,养成习惯 文章每周持续更新,可以微信搜索「herongwei」第一时间阅读和催更,本文 GitHub : https://github.com/rongweihe/Mor ...
- STL源码剖析之序列式容器
最近由于找工作需要,准备深入学习一下STL源码,我看的是侯捷所著的<STL源码剖析>.之所以看这本书主要是由于我过去曾经接触过一些台湾人,我一直觉得台湾人非常不错(这里不涉及任何政治,仅限 ...
- STL"源码"剖析-重点知识总结
STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略多 :) 1.STL概述 STL提供六大组件,彼此可以组合 ...
随机推荐
- 判断qq浏览器和uc浏览器?
判断在iphone手机上打开的是uc浏览器还是qq浏览器 <html lang="en"> <head> <meta charset="ut ...
- 利用Wireshark抓取并分析OpenFlow协议报文
OpenFlow 交换机与控制器交互步骤 1. 利用Mininet仿真平台构建如下图所示的网络拓扑,配置主机h1和h2的IP地址(h1:10.0.0.1,h2:10.0.0.2),测试两台主机之间的网 ...
- 设计模式-Iterator
本文参(chao)考(xi)<图解设计模式> 结城浩 (作者) 杨文轩 (译者) 1.Iterator 模式 迭代器作用于集合,是用来遍历集合元素的对象. 迭代器模式提供一种方法顺序访问一 ...
- T-MAX--冲刺合集
目录 设想和目标 计划 资源 变更管理 设计/实现 测试/发布 团队的角色,管理,合作 总结 照片 各组员对于最终项目成果的贡献度 这个作业属于哪个课程 2019秋福大软件工程实践Z班 (福州大学) ...
- pt-table-checksum校验与pt-table-sync修复数据【转】
1:下载工具包 登录网站下载相应的工具包 https://www.percona.com/downloads/percona-toolkit/LATEST/ 2:安装 (1)yum安装: sudo y ...
- 在Android Studio中下载Android SDK的两种方式(Android Studio3.0、windows)
方式一:网站下载:https://androidsdkmanager.azurewebsites.net/SDKPlatform 方式二.设置HTTP Proxy1. 打开Settings2. 点击H ...
- Java基础 switch 表达式为字符串
JDK :OpenJDK-11 OS :CentOS 7.6.1810 IDE :Eclipse 2019‑03 typesetting :Markdown code ...
- 从0开始学爬虫8使用requests/pymysql和beautifulsoup4爬取维基百科词条链接并存入数据库
从0开始学爬虫8使用requests和beautifulsoup4爬取维基百科词条链接并存入数据库 Python使用requests和beautifulsoup4爬取维基百科词条链接并存入数据库 参考 ...
- netty5拆包问题解决实例
基于之前的例子(参见netty5自定义私有协议实例),我们修改下客户端handler,让发送的数据超过1024字节: NettyClientHandler: /** * 构造PCM请求消息体 * * ...
- Qt编写气体安全管理系统20-控制器管理
一.前言 控制器管理,主要就是对控制器进行添加删除和修改,其中包括编号.端口名称.控制器名称.控制器地址.控制器型号.探测器数量这几个字段,端口名称表示当前控制器所属哪个端口,一个系统中可以有好多个端 ...