《Effective C++》再次探索traits技法
首先介绍C++标准程序库中的五种迭代器,关于这个可以看我的另一个笔记:http://blog.csdn.net/m0_37316917/article/details/70053513。
对于这五种分类,C++标准程序库分别提供专属的标志结构加以确认:
struct input_iterator_tag{};
struct output_iterator_tag{};
struct forward_iterator_tag{}:public input_iterator_tag{};
struct bidirectional_tag:public forward_iterator_tag{};
struct random_access_iterator_tag:public bidirectional_iterator_tag{};
这些struct之间的继承关系是有效的is-a的挂系,所有forward迭代器都是input迭代器,以此类推。
traits并不是C++关键字或者一个预先定义好的构件,它们是一种技术,也是C++程序员共同遵守的协议,这个技术的要求之一是,它对内置类型和用户自定义类型的表现必须一样好,比如下面的advance函数:
template<typename IterT,typename DistT>
void advance(IterT&iter,Dist d)
{
if(iter is a random access iterator)
{
iter+=d;
}
else
{
if(d>=0)
{while(d--)++iter;}
else
{while(d++)--iter;}
}
}
advance()如果收到的实参是一个指针(例如const char*)和一个int,上述advance仍然必须有效运作,那意味着traits技术也必须能够施行于内置类型比如指针身上。
“traits必须能够施行与内置类型”意味着“类型内的嵌套信息(nesting information)”这种东西出局了,因为我们无法将信息嵌套于原始指针中国,因此类型的traits信息必须位于类型自身之外,标准技术是将它放进一个template及其一个或者多个特化版本中,这样的templates在标准程序库中有多个,其中针对迭代器者被命名为iterator_traits:
template<typename IterT>
struct iterator_traits;//迭代器分类的相关信息
如你所限,iterator_traits是个struct,西瓜上traits纵使被实现为struct,但它们却又被成为traits classes。
iterator_traits的运作方式是,针对每一个类型IterT,在struct iterator_traits内一定声明某个typedef名为iterator_category,这个typedef用来确认IterT的迭代器分类。
为了支持内置类型指针迭代器。Iterator_traits特别针对指针类型提供一个偏特化版本,由于指针的行为和random access迭代器累死,所以iterator_traits为指针指定的迭代器类型是:
template<typename IterT>
struct iterator_traits<IterT*>
{
typedef random_access_iterator_tag iterator_category;
};
现在我们可以对advance实践之前的伪代码。
template<typename IterT,typename DistT>
void advance(IterT&iter,DIstT d){
if(typeid(typename std::iterator_traits<IterT>::iterator_category)==typeif(std::random_access_iterator_tag))
......;//do something
}
但是这种方法将会导致编译问题,IterT的类型在编译期间获得,所以iterator_traits::iterator::category也可在编译期间确定,但if语句是在运行期才会核定,为什么将可在编译期完成的事情延迟到运行期才完成呢?这不仅浪费实践,也造成可执行代码的膨胀。
我们需要一个办法在编译期期间核定类型成功,这个办法就是重载:
template<typename IterT,typename DistT>
void doadvance(IterT&iter,DIstT d,std::random_access_iterator_tag)//用于实现random access迭代器
{
iter+=d;
}
template<typename IterT,typename DistT>
void doadvance(IterT&iter,DIstT d,std::bidirectional_iterator_tag)//用于实现bidirectional迭代器
{
if(d>=0) {while(d--) ++iter;}
else{while(d++)--iter;}
}
template<typename IterT,typename DistT>
void doadvance(IterT&iter,DIstT d,std::input_iterator_tag)//用于实现input迭代器
{
if(d<0)
throw std::out_of_range("Nagetive distance");
while(d--)++iter;
}
由于forward_iterator_tag继承自input_iterator_tag,所以上述doadvance的input_iterator_tag版本也能够处理forward迭代器,这是iterator_tag structs继承关系带来的一项红利,实际上这也是public继承带来的部分好处:针对base class编写的代码用于derived class也行得通。
有了这些doadvance重载版本,advance需要做的只是调用它们并且额外传递一个对象,后者必须带有适当的迭代器分类,于是编译期运用重载解析几只调用适当的实现代码:
template<typename IterT,typename DistT>
void advance(IterT&iter,DistT d)
{
doadvance(iter,d,typename std::iterator_traits<IterT>::iterator_category());
}
《Effective C++》再次探索traits技法的更多相关文章
- C++中的Traits技法
Traits广泛应用于标准程序库.Traits classes使得"类型相关信息"在编译期可用. 认真读完下面的示例,你应该就懂了Traits技法,其实并不难. #include ...
- C++ traits技法的一点理解
为了更好的理解traits技法.我们一步一步的深入.先从实际写代码的过程中我们遇到诸如下面伪码说起. template< typename T,typename B> void (T a, ...
- 带你深入理解STL之迭代器和Traits技法
在开始讲迭代器之前,先列举几个例子,由浅入深的来理解一下为什么要设计迭代器. //对于int类的求和函数 int sum(int *a , int n) { int sum = 0 ; for (in ...
- STL源代码剖析(二) - 迭代器与traits技法
提要 先看一段用迭代器的代码: int a[] = {1, 2, 3, 4, 5}; vector<int> v1( a, a+5); vector<int>::iterato ...
- 仿SGI STL的traits技法
首先是iterator traits,这个是用来萃取迭代器的特性的 #ifndef _STL_ITERATOR_H_ #define _STL_ITERATOR_H_ #include <cst ...
- Traits技法
扮演"特性萃取机"角色,萃取各个迭代器的特性(迭代器的相应类型) 模板特例化:提供一份template定义式,而其本身仍为templatization 通过class templa ...
- STL源码--iterator和traits编程技法
第一部分 iterator学习 STL iterators定义: 提供一种方法,使之能够依序巡访某个聚合物(容器)所含的各个元素,而又无需暴露该聚合物的内部表达方式. 任何iteartor都应该提供5 ...
- [转载]《STL源码剖析》阅读笔记之 迭代器及traits编程技法
本文从三方面总结迭代器 迭代器的思想 迭代器相应型别及traits思想 __type_traits思想 一 迭代器思想 迭代器的主要思想源于迭代器模式,其定义如下:提供一种方法,使之能够依 ...
- 对C++ templates类模板的几点补充(Traits类模板特化)
前一篇文章<浅谈C++ templates 函数模板.类模板以及非类型模板参数>简单的介绍了什么是函数模板(这个最简单),类模板以及非类型模板参数.本文对类模板再做几点补充. 文章目录1. ...
- STL内存管理
1. 概述 STL Allocator是STL的内存管理器,也是最低调的部分之一,你可能使用了3年stl,但却不知其为何物. STL标准如下介绍Allocator the STL includes s ...
随机推荐
- 1.RabbitMQ系列之服务启动
1. docker方式启动MQ # latest RabbitMQ 3.10 docker run -it --rm --name rabbitmq -p 5672:5672 -p 15672:156 ...
- C# String.IsNullOrEmpty()方法的使用
IsNullOrEmpty(string)是String类的一个有参的方法,方法需要类的调用,所以String.IsNullOrEmpty(string) IsNullOrEmpty是判断字符串的Nu ...
- awk模式pattern
awk模式pattern 再来回顾下awk的语法 awk [option] 'pattern[action]' file ... awk是按行处理文本,刚才讲解了print动作,现在讲解特殊的patt ...
- MyBatis笔记03------XXXMapper.xml文件解析
SQL映射文件的配置解析 当我们写好mapper(dao)层接口时,然后在对应的XXXMapper.xml文件中写业务逻辑对应的SQL映射语句,通过这个文件中可以实现CRU操作,那么下面说明如何编写这 ...
- 题解合集 (update on 11.5)
收录已发布的题解 按发布时间排序. 部分可能与我的其他文章有重复捏 qwq . AtCoder for Chinese: Link ZHOJ: Link 洛谷 \(1\sim 5\) : [题解]CF ...
- Java:ArrayList的基本使用(学习笔记)
集合和数组的对比(为什么要有集合) 分为俩点 1. 长度:数组的长度是固定的,集合的长度是可变的. 2. 存储类型: 数组:可以存储基本数据类型,引用数据类型. 集合:只能存储引用数据类型. 小t ...
- beego-orm-sqlite
dao.go ``` package daoimport ( "fmt" "github.com/astaxie/beego/orm" "html&q ...
- mysql 多个结构不同表查询 返回相同字段名
( select ID,数据库原字段名1 AS 统一字段名1,数据库原字段名2 AS 统一字段名2 from 第一个表名 WHERE 1) UNION(联合表查询)( select ID,数据库原字段 ...
- (C++) 初始化列表 std::initializer_list
构造时直接使用初始化列表 T object { arg1, arg2, ... }; (1) T { arg1, arg2, ... } (2) new T { arg1, arg2, ... } ( ...
- Vue3“直接”修改props
父组件 import { reactive } from 'vue'; //对话框数据 const show = reactive({ isshow: false, }); //打开对话框 const ...