【C++STL学習と応用まとめ】19:反復器特性-iterator traits


このシリーズの文章の目次はここです:目次.カタログを通してSTLの全体的な理解を得ることができます
前言
本論文では,異なる反復器タイプを定義し区別するために用いられるSTLにおける反復器関連のタイプと特性を紹介した.iterator tagが反復器の「ラベル」として反復器のタイプを区別するために使用される場合.iterator traitsは、すべてのタイプの反復器が持つべき共通情報を定義します.では、標準ライブラリはなぜこれらのものを提供していますか?答えは,これらの情報に基づいて汎用コードを記述し,汎用コードでiterator traitsに基づいて反復器のタイプを判定して対応する処理を行い,反復器を自分で定義してカスタム反復器操作を実現できることである.
iterator tag
次のコードでは、オブジェクト向けの「IS A」の意味を含むいくつかのタイプの継承関係を持つ5つの反復器の「ラベル」について説明します.例えば、forward_iterator_tagbidirectional_terator_tagの継承関係から、それぞれ対応する反復器タイプ、Bidirectional Iterator"IS A"Forward Iteratorが分かる.Forward Iteratorの場所を使って、Bidirectional Iteratorをなくしてもいいという意味です.
namespace std
{
    struct output_iterator_tag {};

    struct input_iterator_tag {};

    struct forward_iterator_tag : public input_iterator_tag {};

    struct bidirectional_iterator_tag : public forward_iterator_tag {};

    struct random_access_iterator_tag : public bidirectional_iterator_tag {};
}

iterator traits
iterator traits:反復器のプロパティ.すべての反復器に共通のタイプ情報を定義します.
namespace std
{
    template <typename T>
    struct iterator_traits
    {
        typedef typename T::iterator_category   iterator_category;
        typedef typename T::value_type          value_type;
        typedef typename T::difference_type     difference_type;
        typedef typename T::pointer             pointer;
        typedef typename T::reference           reference;
    };
}
iterator_traitsがあれば、反復器(タイプT)が指す値タイプの変数を定義できます.std::iterator_traits<T>::value_type val;
iterator traitsを使用して汎用コードを記述する
反復器を使用して定義されたデータ型(value_typeなど)
アルゴリズム内で反復器を使用して定義されたデータ型value_type
template <typename T>
void shift_left(T beg, T end)
{
    typedef typename std::iterator_traits<T>::value_type value_type;

    if (beg != end) 
    {
        value_type temp(*beg);
        // other operations...
    }
}

反復器分類iterator_categoryの使用
ステップ2:
  • は、定義されたテンプレート関数fの内部で、iterator_categoryを追加パラメータとする別の関数fを呼び出し、差別化処理を完了する.
  • は、特定のタイプの反復器に対して特別な処理を行うために、異なるタイプのiterator_categoryパラメータを使用して、第1のステップで再ロードされたfを実現する.
  • template <typename Iterator>
    void f(Iterator beg, Iterator end)
    {
        f(beg, end, std::iterator_traits<Iterator>::iterator_category());
    }
    
    // special f for random-access iterators.
    template <typename RandomIterator>
    void f(RandomIterator beg, RandomIterator end, std::random_access_iterator_tag)
    {
        //...
    }
    
    // special f for bidirectional terators.
    template <typename BidirectionalIterator>
    void f(BidirectionalIterator beg, BidirectionalIterator end, std::bidirectional_iterator_tag)
    {
        // ...
    }

    上記の2つの特殊なリロードバージョンのfは、ランダム反復器と双方向反復器に対してそれぞれ特殊な処理を行う.前述したiterator tagの継承関係により、ある親クラスに対してのみ1つのfを定義することができ、その親クラスのすべての子クラスが同じfを共有することができる.たとえば、次の例です.
    distance()関数の実装:
    //    ,        ,       ,   `iterator_category`        。
    template <typename Iterator>
    typename std::iterator_traits<Iterator>::difference_type distance(Iterator pos1, Iterator pos2)
    {
        return distance(pos1, pos2, std::iterator_traits<Iterator>::iterator_category());
    }
    
    //    ,           
    // 1. for random-access iterators.
    template <typename RandomIterator>
    typename std::iterator_traits<RandomIterator>::difference_type
    distance(RandomIterator pos1, RandomIterator pos2, std::random_access_iterator_tag)
    {
        return pos2 - pos1;
    }
    
    // 2. for other iterators.
    template <typename InputIterator>
    typename std::iterator_traits<InputIterator>::difference_type
    distance(InputIterator pos1, InputIterator pos2, std::input_iterator_tag)
    {
        typename std::iterator_traits<InputIterator>::difference_type d;
        for (d=0; pos1 != pos2; ++pos1, ++d) { }
        return d;
    }

    distance()の実装から,ランダム反復器は反復器の算術演算pos 2−pos 1を用いて問題を解決することが分かった.2番目のバージョンのdistance()は、Input Iterator、Forward Iterator、Bidirectional Iteratorの3種類の反復器と同時に機能します.これは、冒頭で与えられたiterator tagの継承関係によって決まります.
    カスタム反復
    前の紹介から分かるように、自分で反復器を書くには、iterator_traitsの5つのタイプが必要です.このニーズは、次の2つの方法で実現できます.
  • 私の反復器タイプでは、iterator_traitsに必要な5つのものを定義します.
  • の配列特化バージョンのように、iterator_traitsの特化または偏特化テンプレートiterator_traitsが特化されている.

  • 次に、「c++標準ライブラリ」の一例を使用して、カスタム反復器を実装するために最初の方法を説明します.
    C++標準は特殊なベースクラスを提供しています.iterator<>です.5つのタイプの定義を完了します.このベースクラスを継承し、必要なタイプパラメータを指定するだけでいいです.例えば、次のように定義します.
    class MyIterator : public std::iterator<std::forward_iterator_tag, type, std::ptrdiff_t, type*, type&)
    {
        // ...
    };

    最初のパラメータは、反復器の分類を指定します.forward_iterator_tagbidirectional_iterator_tag、またはrandom_access_iterator_tagなどです.
    2番目のパラメータは、要素タイプを指定します:value_type3番目のパラメータは、位置差のタイプを指定します:difference_type4番目のパラメータはポインタタイプを指定します:pointer、5番目のパラメータは参照タイプを指定します:reference最後の3つのパラメータのデフォルト値は、それぞれptrdiff_ttype*type&であり、使用時に無視できます.
    次の例は、std::inserterのような関連コンテナのinserterアダプタを定義した本の例です.std::iteratorに対して、位置パラメータを省き、コンテナに転送すればいいです.
    //----------------------- associative container inserter ----------------------
    template <typename Con>
    class asso_inserter_iterator 
        : public iterator<output_iterator_tag, typename Con::value_type>
    {
    public:
        explicit asso_inserter_iterator(Con &con) : conRef_(con) {}
    
        asso_inserter_iterator<Con>& operator= (const typename Con::value_type &val)
        {
            conRef_.insert(val);
            return *this;
        }
    
        asso_inserter_iterator<Con>& operator * () 
        {
            return *this;
        }
    
        asso_inserter_iterator<Con>& operator ++ ()
        {
            return *this;
        }
    
        asso_inserter_iterator<Con>& operator ++ (int)
        {
            return *this;
        }
    
    protected:
        Con     &conRef_;
    };
    
    // convenience function to create asso_inserter_iterator.
    template <typename Con>
    inline asso_inserter_iterator<Con> asso_inserter(Con &con)
    {
        return asso_inserter_iterator<Con>(con);
    }

    使用方法:
    RUN_GTEST(UserDefinedIterator, AssoInserter, @);
    
    set<int> uset;
    
    asso_inserter_iterator<set<int>>  ainserter(uset);
    
    *ainserter = 10;
    ++ainserter;
    *ainserter = 20;
    ++ainserter;
    *ainserter = 30;
    
    printContainer(uset, "uset: ");     // 10 20 30
    
    asso_inserter(uset) = 1;
    asso_inserter(uset) = 2;
    printContainer(uset, "uset: ");     // 1 2 10 20 30
    
    array<int, 5> a = {11, 22, 33, 44, 55};
    copy(a.begin(), a.end(), asso_inserter(uset)); // 1 2 10 11 20 22 30 33 44 55
    printContainer(uset, "uset: ");
    
    END_TEST;

    ここでの用法例は他の反復器アダプタと同様である、【C++STL学習と応用まとめ】18:反復器アダプタの使い方を参照する.
    ソースおよびリファレンスリンク
  • user_defined_iterator.cpp
  • iterator_category
  • iterator_traits

  • 作者のレベルは有限で、関连する知识の理解と総括に対してどうしても间违いがあって、また指摘することを望んで、とても感谢します!
    githubブログ、CSDNブログ、ようこそ