web-dev-qa-db-ja.com

C ++のカスタムイテレータ

TItemsクラスへのいくつかのstlコレクションポインタの集合であるクラスTContainerがあります。

内部処理のクライアントを抽象化するTContainerクラスのすべてのコレクションの要素をトラバースするイテレーターを作成する必要があります。

これを行うにはどうすればよいでしょうか。イテレータを拡張するクラスを作成する必要がある場合(そうする場合、どのイテレータクラスを拡張する必要がありますか)、イテレータの集約であるイテレータクラスを作成する必要がありますか?

FORWARD_ONLYイテレータのみが必要です。

つまり、これが私のコンテナの場合:

typedef std::vector <TItem*> ItemVector;
class TContainer {
   std::vector <ItemVector *> m_Items;
};

M_Itemsメンバー変数のベクターに含まれるすべての項目をトラバースするための適切なイテレーターは何でしょうか。

29
Sugar

自分のイテレータを作成したとき(少し前のことです)、std :: iteratorから継承し、最初のテンプレートパラメータとしてタイプを指定しました。お役に立てば幸いです。

順方向反復子の場合、次のコードでinput_iterator_tagではなくforward_iterator_tagを使用します。

このクラスは、もともとistream_iteratorクラスから取得されました(そして、自分用に変更されたため、istram_iteratorに似ていない可能性があります)。

template<typename T>
class <PLOP>_iterator
         :public std::iterator<std::input_iterator_tag,       // type of iterator
                               T,ptrdiff_t,const T*,const T&> // Info about iterator
{
    public:
        const T& operator*() const;
        const T* operator->() const;
        <PLOP>__iterator& operator++();
        <PLOP>__iterator operator++(int);
        bool equal(<PLOP>__iterator const& rhs) const;
};

template<typename T>
inline bool operator==(<PLOP>__iterator<T> const& lhs,<PLOP>__iterator<T> const& rhs)
{
    return lhs.equal(rhs);
}

イテレータタグに関するこのドキュメントを確認してください。
http://www.sgi.com/tech/stl/iterator_tags.html

イテレータの情報をもう一度読んだだけの場合:
http://www.sgi.com/tech/stl/iterator_traits.html

これは従来の方法(iterator_tags)であり、より現代的なアプローチは、イテレーターがiterator_traits <>をセットアップしてSTLと完全に互換性を持つようにすることです。

31
Martin York

Boostにアクセスできる場合は、 iterator_facade は最も堅牢なソリューションであり、使い方は非常に簡単です。

22
James Hopkin

最初に少し一般化しましょう:

_typedef int value_type;
typedef std::vector<value_type*> inner_range;
typedef std::vector<inner_range*> outer_range;
_

イテレータ:

_struct my_iterator : std::iterator_traits<inner_range::iterator> 
{
    typedef std::forward_iterator_tag iterator_category;

    my_iterator(outer_range::iterator const & outer_iterator, 
                outer_range::iterator const & outer_end)
    : outer_iterator(outer_iterator), outer_end(outer_end)
    { 
        update();
    }

    my_iterator & operator++()
    {
        ++inner_iterator;
        if(inner_iterator == inner_end)
        {
            ++outer_iterator;
            update();
        }
        return *this;
    }

    reference operator*() const
    {   
        return *inner_iterator;
    }

    bool operator==(my_iterator const & rhs) const
    {   
        bool lhs_end = outer_iterator == outer_end;
        bool rhs_end = rhs.outer_iterator == rhs.outer_end;
        if(lhs_end && rhs_end)
            return true;
        if(lhs_end != rhs_end)
            return false;
        return outer_iterator == rhs.outer_iterator 
            && inner_iterator == rhs.inner_iterator;
    }

private:

    outer_range::iterator outer_iterator, outer_end;
    inner_range::iterator inner_iterator, inner_end;

    void update()
    {
        while(outer_iterator != outer_end)
        {
            inner_iterator = (*outer_iterator)->begin();
            inner_end = (*outer_iterator)->end();
            if(inner_iterator == inner_end)
                ++outer_iterator;
            else
                break;
        }
    }    
};
_

このクラスは、外部イテレーターに内部範囲へのポインターが含まれていることを前提としています。これは、質問の要件でした。これは、updateメンバーのbegin()およびend()の前の矢印に反映されています。これらの矢印をドットで置き換えると、外部イテレーターに値による内部範囲が含まれる、より一般的な状況でこのクラスを使用できます。ちなみに、このクラスは内部範囲にポインタが含まれているという事実にとらわれないことに注意してください。クラスのクライアントだけがそれを知る必要があります。

_boost::iterator_facade_を使用する場合、コードは短くなる可能性がありますが、それほど単純なものにブースト依存関係を追加する必要はありません。その上、トリッキーな部分は等値演算とインクリメント演算だけなので、とにかくそれらをコーディングする必要があります。

次のボイラープレートメンバーを「読者のための演習」として残しました。

  • 後置インクリメント反復子
  • 演算子!=
  • デフォルトのコンストラクタ
  • 演算子->

別の興味深い演習は、これを任意のコンテナで機能するテンプレートに変えることです。いくつかの場所にtypename注釈を追加する必要があることを除いて、コードは基本的に同じです。

使用例:

_int main()
{
    outer_type outer;
    int a = 0, b = 1, c = 2;
    inner_type inner1, inner2;
    inner1.Push_back(&a);
    inner1.Push_back(&b);
    inner2.Push_back(&c);
    outer.Push_back(&inner1);
    outer.Push_back(&inner2);

    my_iterator it(outer.begin(), outer.end());
                e(outer.end(), outer.end());
    for(; it != e; ++it)
        std::cout << **it << "\n";
}
_

どのプリント:

0 1 2

18
Manuel

イテレータは、特定のインターフェースをサポートする単なるクラスです。少なくとも、次のことができるようにする必要があります。

  • インクリメントおよび/またはデクリメント
  • それを逆参照して、それが「指す」オブジェクトを取得する
  • 平等と不平等をテストする
  • コピーして割り当てます

コレクションに対してそれを適切に行うことができるクラスを作成したら、イテレータを返す関数を持つようにコレクションを変更する必要があります。少なくともあなたは欲しくなるでしょう

  • 最初の要素に配置された新しいイテレータ型のインスタンスを返すbegin()関数
  • コンテナー内の項目の最後を過ぎて(おそらく概念的に)配置されたイテレーターを返すend()関数
6
anon

ビューテンプレートライブラリ を確認します。

特にチェック

  1. ユニオンビュー 連結された2つのコンテナを表示します。
  2. 連結ビュー 連結されたコンテナのコレクションを表示します。
1
Nitin Bhide

これは私が作成できた最も単純なコードです(カスタムイテレータ用)。私はこの領域を探索し始めたばかりであることに注意してください。これは、組み込みupper_bound関数を呼び出して、整数関数(例としてx^2)に対してバイナリ検索を実行します。

#include <algorithm>
#include <iostream>

using namespace std;

class Iter
{
  public:
  int x;
  Iter() { x = -1; }
  Iter(int a) { x = a; }

  bool operator!=(Iter &i2) const { return x != i2.x; }
  void operator++() { x++; }
  void operator+=(int b) { x += b; }
  int operator-(const Iter &i2) const { return x - i2.x; }
  int operator*() const {
    cout << "calculating for x " << x << endl;
    return x*x;
  }

  typedef random_access_iterator_tag iterator_category;
  typedef int value_type;
  typedef int difference_type;
  typedef int* pointer;
  typedef int& reference;
};

main ()
{
  ios::sync_with_stdio(false);
  cout << upper_bound(Iter(0), Iter(100), 40).x << endl;
}

// :collapseFolds=1:folding=explicit:

そして、これは出力がどのように見えるかです:

calculating for x 50
calculating for x 25
calculating for x 12
calculating for x 6
calculating for x 9
calculating for x 8
calculating for x 7
7
0
Jarekczek