《Effective C++》读书笔记之item47:请使用traits classes表现类型信息
2012-08-30 19:53
519 查看
1.使用traits技术可以在编译期间获取某些类型信息,它要求对内置类型和用户自定义类型表现得一样好。标准模板库是把traits信息放到模板中,其中针对迭代器的被命名为iterator_traits,它是一个结构体:
它的工作原理是:针对每一个类型IterT,在结构体中声明某个typedef名为iterator_category,这个typedef用于确认IterT的迭代器分类。它包括两部分实现:
(1)对于自定义类型,用户必须在类模板中声明一个typedef名为iterator_category,比如对双端队列和列表的模板类:
在iterator_traits中,获取迭代器的类型信息:
(2)对于内置类型的指针而言,iterator_traits提供一个偏特化版本:
因此,设计并实现traits class的步骤是:
(1)确认若干希望将来可取得的类型相关信息(例如对于迭代器,希望获取它的分类(见下))。
(2)为该信息选择一个名称(例如iterator_category)。
(3)提供一个模板和一组特化版本(如iterator_traits),内含希望支持的类型相关信息。
2.traits class的使用。如上所述的类型信息可以 在编译期间获取,但是此时需要对传递来的指针类型做出判断,如果使用if...else...分支判断语句,只有在运行期间才能获取。怎么办?
可以使用函数重载的办法:重载函数需要在编译期间通过确定参数类型来决定调用哪个版本,正好可以解决“编译期条件句”的问题。
PS:这个想法实在太牛了!
如前所述的迭代器类型问题,可以设计多个重载函数做适配于各个类型的操作,然后在同一个函数中调用它们。
可见,使用traits class的方法是:
(1)建立一组重载函数或函数模板,彼此间的差异只在于各自的traits参数,令每个函数实现代码与其接受的traits信息相适配。
(2)建立一个控制函数或函数模板,它调用上述重载函数并传递traits class所提供的信息。
3.STL中的迭代器分为5类:
(1)input迭代器:模仿指向输入文件的阅读指针。只能向前移动,一次一步,客户能且只能读取一次所指的东西。典型代表是istream_iterator。
(2)output迭代器:模仿输出,只能向前移动,一次一步,客户只能改写所指的东西,只能改写一次。典型代表是ostream_iterator。
(3)forward迭代器:可做input和output的所有事情,可以读写所指物一次以上,如单向链表。
(4)bidirectional迭代器:可以向前向后移动,如STL中的list、set、multiset、map和multimap迭代器。
(5)random access迭代器:可以执行“迭代器算术”,即将指针向前向后移动任意长度,类似于内置指针。如vector、deque和string迭代器。
它们的继承关系(这里是卷标结构)如图所示,是一种公有继承体系:
template<typename IterT> struct iterator_traits;
它的工作原理是:针对每一个类型IterT,在结构体中声明某个typedef名为iterator_category,这个typedef用于确认IterT的迭代器分类。它包括两部分实现:
(1)对于自定义类型,用户必须在类模板中声明一个typedef名为iterator_category,比如对双端队列和列表的模板类:
template<typename ...> class deque{ public: iterator{ public: typedef random_access_iterator_tag iterator_category; //双端队列的迭代器可以随机访问 ... }; template<typename ...> class list{ public: iterator{ public: typedef bidirectional_access_iterator_tag iterator_category; //列表的迭代器可以随机访问 ... };
在iterator_traits中,获取迭代器的类型信息:
template<typename IterT> struct iterator_traits{ typedef typename IterT::iterator_category iterator_category; ... };
(2)对于内置类型的指针而言,iterator_traits提供一个偏特化版本:
template<typename IterT> struct iterator_traits<IterT*>{ typedef random_access_iterator_tag iterator_category; //内置类型的指针类型与random access迭代器类似 ... };
因此,设计并实现traits class的步骤是:
(1)确认若干希望将来可取得的类型相关信息(例如对于迭代器,希望获取它的分类(见下))。
(2)为该信息选择一个名称(例如iterator_category)。
(3)提供一个模板和一组特化版本(如iterator_traits),内含希望支持的类型相关信息。
2.traits class的使用。如上所述的类型信息可以 在编译期间获取,但是此时需要对传递来的指针类型做出判断,如果使用if...else...分支判断语句,只有在运行期间才能获取。怎么办?
可以使用函数重载的办法:重载函数需要在编译期间通过确定参数类型来决定调用哪个版本,正好可以解决“编译期条件句”的问题。
PS:这个想法实在太牛了!
如前所述的迭代器类型问题,可以设计多个重载函数做适配于各个类型的操作,然后在同一个函数中调用它们。
template<typename IterT, typename DistT> void advance(IterT&, DistT d){ doAdvance{ //doAdvance()函数有多个重载版本,这些版本的不同之处在于传递的迭代器的参数不同,见下 iter, d, typename std::iterator_traits<IterT>::iterator_category() ); .. }; template<typename IterT, typename DistT> //这份实现用于random access迭代器 void doAdvance(IterT& iter, Dist d, std::random_access_iterator_tag) { iter+=d; } template<typename IterT, typename DistT> //这份实现用于bidirectional迭代器 void doAdvance(IterT& iter, Dist d, std::bidirectional_iterator_tag) { if(d>=0){ while(d--) ++iter; } else{ while(d++) --iter; } } } template<typename IterT, typename DistT> //这份实现用于input迭代器,由于forward迭代器派生于input迭代器,也可用于forward迭代器 void doAdvance(IterT& iter, Dist d, std::input_iterator_tag) { if(d<0){ throw std::out_out_range("Negnative distance"); //对于input或forward迭代器,移动负距离会导致不明确的行为,因而抛出异常 } while(d--) ++iter; }
可见,使用traits class的方法是:
(1)建立一组重载函数或函数模板,彼此间的差异只在于各自的traits参数,令每个函数实现代码与其接受的traits信息相适配。
(2)建立一个控制函数或函数模板,它调用上述重载函数并传递traits class所提供的信息。
3.STL中的迭代器分为5类:
(1)input迭代器:模仿指向输入文件的阅读指针。只能向前移动,一次一步,客户能且只能读取一次所指的东西。典型代表是istream_iterator。
(2)output迭代器:模仿输出,只能向前移动,一次一步,客户只能改写所指的东西,只能改写一次。典型代表是ostream_iterator。
(3)forward迭代器:可做input和output的所有事情,可以读写所指物一次以上,如单向链表。
(4)bidirectional迭代器:可以向前向后移动,如STL中的list、set、multiset、map和multimap迭代器。
(5)random access迭代器:可以执行“迭代器算术”,即将指针向前向后移动任意长度,类似于内置指针。如vector、deque和string迭代器。
它们的继承关系(这里是卷标结构)如图所示,是一种公有继承体系:
相关文章推荐
- Effective C++ 条款47 请使用traits classes表现类型信息
- Effective C++ Item 47 请使用 traits classes 表现类型信息
- Effective C++ Item 47 请使用 traits classes 表现类型信息
- effective C++ 条款 47:使用traits classes表现类型信息
- Effective C++ 条款 47:使用traits classes表现类型信息
- Effective C++ -----条款47:请使用traits classes表现类型信息
- C++之使用traits classes表现类型信息(47)---《Effective C++》
- 【47】请使用traits classes表现类型信息
- [翻译] Effective C++, 3rd Edition, Item 47: 为类型信息使用 traits classes(特征类)(上)
- Item 47: 请使用traits class表现类型信息
- [翻译] Effective C++, 3rd Edition, Item 47: 为类型信息使用 traits classes(特征类)(下)
- 条款47:请使用traits classes表现类型信息(1)
- 条款47:请使用traits classes表现类型信息(2)
- 条款47:请使用traits classes表现类型信息
- 条款47:请使用traits classes 表现类型信息
- 读书笔记 effective c++ Item 47 使用traits class表示类型信息
- Item 47:使用Traits类提供类型信息
- Item 47:使用Traits类提供类型信息
- 条款47:请使用traits class表示类型信息
- 《Effective C++》读书笔记之item45:运用成员函数模板接受所有兼容类型