读书笔记_Effective_C++_条款四十七:请使用trait classes来表示类型信息
这一条款主要来讨论模板中迭代器的属性iterator_category,它可以通过类似于vector<int>::iterator::iterator_category的方式来取得。
到这里我们有必要学习一下STL迭代器的类型,总共有五种,分别是:
input_iterator:只读,只能逐个前移
output_iterator:只写,只能逐个前移
forward_iterator:可读可写,只能逐个前移
bidirectional_iterator:可读可写,支持逐个前移和后移
random_access_iterator:可读可写,支持随机访问(任意步数移动)
为了表明容器内使用的是哪一种迭代器,STL在定义迭代器会总会打个一个标记“tag”,每个tag都是一个空的结构体,对应于以上五种迭代器,tag也有五个:
struct input_iterator_tag{};
struct output_iterator_tag{};
struct forward_iterator_tag: public input_iterator_tag{};
bidirectional_iterator: public forward_iterator_tag{};
random_access_iterator: public bidirectional_iterator_tag{};
注意这五个tag之间,有些存在继承关系。
这个标记有什么用呢?STL在写vector的时候,会这样:
template class <T>
class vector
{
public:
class iterator
{
public:
typedef random_access_iterator iterator_category;
…
}
…
} 写list的时候,会这样写:
template class <T>
class list
{
public:
class iterator
{
public:
typedef bidirectional_iterator iterator_category;
…
}
…
}
既然迭代器已经由tag说明了它的类型(双向的,还是随机访问),那我们如何去利用它呢?比如现在我想要写一个迭代器前移的通用函数DoAdvance,不同迭代器类型会有不同的实现方式,所以我们可以像下面这样:
template <class T>
void DoAdvance(T Container)
{
typedef T::iterator::iterator_category IteratorCategory;
if (typeid(IteratorCategory) == typeid(input_iterator_tag))
{
cout << "Do manner in input_iterator_tag" << endl;
}
else if (typeid(IteratorCategory) == typeid(output_iterator_tag))
{
cout << "Do manner in output_iterator_tag" << endl;
}
else if (typeid(IteratorCategory) == typeid(forward_iterator_tag))
{
cout << "Do manner in forward_iterator_tag" << endl;
}
else if (typeid(IteratorCategory) == typeid(bidirectional_iterator_tag))
{
cout << "Do manner in bidirectional_iterator_tag" << endl;
}
else if (typeid(IteratorCategory) == typeid(random_access_iterator_tag))
{
cout << "Do manner in random_access_iterator_tag" << endl;
}
}
参数T是容器的类型,比如vector<int>,如果像下面这样调用:
vector<int> v;
DoAdvance(v);
那么输出是Do manner in random_access_iterator_tag,因为vector<int>的迭代器是随机访问型的,可以按随机访问类型的处理方式来去实现前移操作。typeid返回结果是名为type_info的标准库类型的对象的引用,它指明了这个对象/定义的类型。
因为这里讨论的是迭代器,所以更常见的是直接传迭代器进去,像这样:
template <class IterT>
void DoAdvance(IterT Iter)
{
typedef IterT::iterator_category IteratorCategory;
if (typeid(IteratorCategory) == typeid(input_iterator_tag))
{
cout << "Do manner in input_iterator_tag" << endl;
}
…
}
注意这里的模板参数是IterT,它表示一个迭代器的类型,比如vector<int>::iterator。这里是去主动访问iterator里面定义的属性iterator_category,我们也可以通过trait classes来访问,像下面这样:
template <class IterT>
void DoAdvance(IterT Iter)
{
if (typeid(iterator_traits<IterT>::iterator_category)
== typeid(input_iterator_tag))
{
cout << "Do manner in input_iterator_tag" << endl;
}
…
}
iterator_traits的定义如下:
template<class IterT>
struct iterator_traits<IterT>
{
typedef typename IterT::iterator_category iterator_category;
…
};
这个感觉只是简化了输入代码量而已,本质上还是去获得迭代器的tag,它有一个针对指针的偏特化版本,像下面这样:
template<class IterT>
struct iterator_traits<IterT*>
{
typedef random_access_iterator_tag iterator_category;
};
这里都是用typeid去进行类型判断的,它是在运行期才能执行,那么能不能放在编译期呢,当然可以,就是要用到函数的重载,像下面这样:
template <class IterT>
void DoAdvance(IterT Iter, input_iterator_tag)
{
cout << "Do manner in input_iterator_tag" << endl;
} template <class IterT>
void DoAdvance(IterT Iter, random_access_iterator_tag)
{
cout << "Do manner in random_access_iterator_tag" << endl;
}
像下面这样使用;
vector<int>::iterator iter;
DoAdvance(iter, iterator_traits<vector<int>::iterator>::iterator_category());
注意迭代器的tag是可以直接作为函数形参的,这样就可以在编译期决定到底执行哪一种迭代器的行为了。
条款标题的traint classes是一个广义的概念,我们之前讨论的iterator_traits只是其一部分,除以之外,还有四份迭代器相关的信息(如value_type等),TR1导入许多新的trait classes,比如is_fundamental<T>等(判断T是否是内置类型)。
最后,我们来总结一下:
1. Traits class使得类型相关信息可以在编译期可用,它们以template和template特化完成实现;
2. 整合重载技术后,traits classes有可能在编译期对类型执行if-else测试。
读书笔记_Effective_C++_条款四十七:请使用trait classes来表示类型信息的更多相关文章
- 读书笔记_Effective_C++_条款四十:明智而审慎地使用多重继承
多重继承是一种比较复杂的继承关系,它意味着如果用户想要使用这个类,那么就要对它的父类也了如指掌,所以在项目中会带来可读性的问题,一般我们都会尽量选择用单继承去替代它. 使用多重继承过程容易碰到的问题就 ...
- 读书笔记_Effective_C++_条款四十六:需要类型转换时请为模板定义非成员函数
这个条款可以看成是条款24的续集,我们先简单回顾一下条款24,它说了为什么类似于operator *这样的重载运算符要定义成非成员函数(是为了保证混合乘法2*SomeRational或者SomeRat ...
- 读书笔记_Effective_C++_条款四十五:运用成员函数模板接受所有兼容类型
比如有一个Base类和一个Derived类,像下面这样: class BaseClass {…}; class DerivedClass : public BaseClass {…}; 因为是父类与子 ...
- 读书笔记_Effective_C++_条款四十一:了解隐式接口和编译期多态
从本条款开始,就进入了全书的第七部分:模板与泛型编程.模板与泛型在C++中是非常重要的部分,还记得本书第一章时,把C++视为一个联邦,它由四个州政府组成,其中一个政府就是模板与泛型了. 本条款是一个介 ...
- 读书笔记_Effective_C++_条款四十九:了解new_handler的行为
本章开始讨论内存分配的一些用法,C/C++内存分配采用new和delete.在new申请内存时,可能会遇到的一种情况就是,内存不够了,这时候会抛出out of memory的异常.有的时候,我们希望能 ...
- 读书笔记_Effective_C++_条款四十八:了解模板元编程
作为模板部分的结束节,本条款谈到了模板元编程,元编程本质上就是将运行期的代价转移到编译期,它利用template编译生成C++源码,举下面阶乘例子: template <int N> st ...
- 读书笔记_Effective_C++_条款四十二:了解typename的双重意义
顾名思义,typename有双重含意.只要你用过template,那么第一重含意一定知道,那就是声明模板的时候,我们既可以这样写: template <class T> 也可以这样写 te ...
- 读书笔记_Effective_C++_条款三十七:绝不重新定义继承而来的缺省参数值
先看下面的例子: enum MyColor { RED, GREEN, BLUE, }; class Shape { public: ; }; class Rectangle: public Shap ...
- 读书笔记_Effective_C++_条款二十七:尽量少做转型动作
有关转型的几种做法,已经在早些的博客中写过了.这里先简单回顾一下,再讲一讲effective中对之更深入的阐述. 转型可以按风格可以分成C风格转型和C++风格转型两大类,C风格转型很容易看到,因为我们 ...
随机推荐
- hibernate中 inverse的用法(转载)
http://blog.csdn.net/xiaoxian8023/article/details/15380529 一.Inverse是hibernate双向关系中的基本概念.inverse的真正作 ...
- GeoHash核心原理解析
http://www.cnblogs.com/LBSer/p/3310455.html 引子 机机是个好动又好学的孩子,平日里就喜欢拿着手机地图点点按按来查询一些好玩的东西.某一天机机到北海公园游玩, ...
- iOS开发-迭代器模式
迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示.开发过程中,我们可能需要针对不同的需求,可能需要以不同的方式来遍历整个整合对象,但是我们不希望 ...
- asp.net的JSON数据进行序列化和反序列化
先要引用在程序集 System.Web.Extensions.dll 的类库,在类中 using System.Web.Script.Serialization 命名空间. 1.定义一个强类型的类(m ...
- js 停止事件冒泡 阻止浏览器的默认行为(阻止超连接 # )
在前端开发工作中,由于浏览器兼容性等问题,我们会经常用到“停止事件冒泡”和“阻止浏览器默认行为”. 1..停止事件冒泡 JavaScript代码 //如果提供了事件对象,则这是一个非IE浏览器if ( ...
- 从零开始编写自己的C#框架 ---- 系列文章
目录: 从零开始编写自己的C#框架(1)——前言从零开始编写自己的C#框架(2)——开发前的准备工作从零开始编写自己的C#框架(3)——开发规范从零开始编写自己的C#框架(4)——文档编写说明从零开始 ...
- NR_OPEN 与 NR_FILE 的区别
NR_OPEN 与 NR_FILE 的区别 阅读0.11版的内核源码时,在linux-0.11/fs/pipe.c中,函数sys_pipe()里面出现了2个宏定义,NR_OPEN 与 NR_FILE. ...
- JavaScript 2016年的概况
国外的网站stateofjs.com根据超过九千位开发人员的问卷调查,发布了2016年JavaScript的年度概况报名. 注:本文翻译的部分可能存在不准确的情况,请以原文为准. 调查结果的报告目录结 ...
- SSH和SFTP简介
传统FTP 在传输机制和实现原理上是没有考虑安全机制的,因为它们在网络上用明文传送数据.用户帐号和用户口令,别有用心的人非常容易地就可以截获这些数据.用户帐 号和用户口令.而且,这些网络服务程序容易受 ...
- latextools \cite 自动补全
最近在用latex写毕业论文,编辑环境用的是Sublime Text 2 加 latextools 插件,在使用latextools的\cite命令来引用参考文献时,我们希望输入\cite{ 后自动弹 ...