• 读书笔记_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的时候,会这样:

     1 template class <T>
     2 class vector
     3 {
     4 public:
     5     class iterator
     6     {
     7     public:
     8         typedef random_access_iterator iterator_category;
     9 10     }
    11 12 }
    13 
    14 写list的时候,会这样写:
    15 template class <T>
    16 class list
    17 {
    18 public:
    19     class iterator
    20     {
    21     public:
    22         typedef bidirectional_iterator iterator_category;
    23 24     }
    25 26 }

    既然迭代器已经由tag说明了它的类型(双向的,还是随机访问),那我们如何去利用它呢?比如现在我想要写一个迭代器前移的通用函数DoAdvance,不同迭代器类型会有不同的实现方式,所以我们可以像下面这样:

     1 template <class T>
     2 void DoAdvance(T Container)
     3 {
     4     typedef T::iterator::iterator_category IteratorCategory;
     5     if (typeid(IteratorCategory) == typeid(input_iterator_tag))
     6     {
     7         cout << "Do manner in input_iterator_tag" << endl;
     8     }
     9     else if (typeid(IteratorCategory) == typeid(output_iterator_tag))
    10     {
    11         cout << "Do manner in output_iterator_tag" << endl;
    12     }
    13     else if (typeid(IteratorCategory) == typeid(forward_iterator_tag))
    14     {
    15         cout << "Do manner in forward_iterator_tag" << endl;
    16     }
    17     else if (typeid(IteratorCategory) == typeid(bidirectional_iterator_tag))
    18     {
    19         cout << "Do manner in bidirectional_iterator_tag" << endl;
    20     }
    21     else if (typeid(IteratorCategory) == typeid(random_access_iterator_tag))
    22     {
    23         cout << "Do manner in random_access_iterator_tag" << endl;
    24     }
    25 }

    参数T是容器的类型,比如vector<int>,如果像下面这样调用:

    1 vector<int> v;
    2 DoAdvance(v);

    那么输出是Do manner in random_access_iterator_tag,因为vector<int>的迭代器是随机访问型的,可以按随机访问类型的处理方式来去实现前移操作。typeid返回结果是名为type_info的标准库类型的对象的引用,它指明了这个对象/定义的类型。

    因为这里讨论的是迭代器,所以更常见的是直接传迭代器进去,像这样:

     1 template <class IterT>
     2 void DoAdvance(IterT Iter)
     3 {
     4     typedef IterT::iterator_category IteratorCategory;
     5     if (typeid(IteratorCategory) == typeid(input_iterator_tag))
     6     {
     7         cout << "Do manner in input_iterator_tag" << endl;
     8     }
     9 10 }

    注意这里的模板参数是IterT,它表示一个迭代器的类型,比如vector<int>::iterator。这里是去主动访问iterator里面定义的属性iterator_category,我们也可以通过trait classes来访问,像下面这样:

     1 template <class IterT>
     2 void DoAdvance(IterT Iter)
     3 {
     4     if (typeid(iterator_traits<IterT>::iterator_category)
     5         == typeid(input_iterator_tag))
     6     {
     7         cout << "Do manner in input_iterator_tag" << endl;
     8     }
     9 10 }

    iterator_traits的定义如下:

    1 template<class IterT>
    2 struct iterator_traits<IterT>
    3 {
    4     typedef typename IterT::iterator_category iterator_category;
    5 6 };

    这个感觉只是简化了输入代码量而已,本质上还是去获得迭代器的tag,它有一个针对指针的偏特化版本,像下面这样:

    1 template<class IterT>
    2 struct iterator_traits<IterT*>
    3 {
    4     typedef random_access_iterator_tag iterator_category;
    5 };

    这里都是用typeid去进行类型判断的,它是在运行期才能执行,那么能不能放在编译期呢,当然可以,就是要用到函数的重载,像下面这样:

     1 template <class IterT>
     2 void DoAdvance(IterT Iter, input_iterator_tag)
     3 {
     4     cout << "Do manner in input_iterator_tag" << endl;
     5 }
     6 
     7 
     8 template <class IterT>
     9 void DoAdvance(IterT Iter, random_access_iterator_tag)
    10 {
    11     cout << "Do manner in random_access_iterator_tag" << endl;
    12 }

    像下面这样使用;

    1 vector<int>::iterator iter;
    2 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测试。

  • 相关阅读:
    领域驱动和MVVM应用于UWP开发的一些思考
    UWP中实现自定义标题栏
    UWP中新加的数据绑定方式x:Bind分析总结
    MVVM框架从WPF移植到UWP遇到的问题和解决方法
    UWP学习目录整理
    MVVM模式解析和在WPF中的实现(六) 用依赖注入的方式配置ViewModel并注册消息
    MVVM模式解析和在WPF中的实现(五)View和ViewModel的通信
    MVVM设计模式和WPF中的实现(四)事件绑定
    MVVM模式解析和在WPF中的实现(三)命令绑定
    MVVM模式和在WPF中的实现(二)数据绑定
  • 原文地址:https://www.cnblogs.com/jerry19880126/p/3669756.html
Copyright © 2020-2023  润新知