イテレータを作成します

多くの場合、独自のイテレータを作成する必要はなく、手元に小さなHowToが必要です。 この記事では、std :: copy、std :: findなどの標準アルゴリズムで使用できる最も単純なイテレータの作成方法を説明します。 c ++ 11およびBOOST_FOREACHのforループでバイパスできるように、コンテナクラスで必要なメソッドと型定義。





コンテナ



コンテナクラスでは、イテレータとconst_iteratorタイプを定義する必要があります(便宜上、最初にタイプが必要です。次に、BOOST_FOREACHを使用したバイパスはそれらなしでは機能しません)。 const_iteratorを返すメソッド):

たとえば、整数の配列を格納するコンテナを取り上げます。

class OwnContainer { public: typedef OwnIterator<int> iterator; typedef OwnIterator<const int> const_iterator; OwnContainer(std::initializer_list<int> values); iterator begin(); iterator end(); const_iterator begin() const; const_iterator end() const; private: const size_t size; std::unique_ptr<int[]> data; };
      
      







OwnContainerメソッドの実装
 OwnContainer::OwnContainer(std::initializer_list<int> values) : size(values.size()), data(new int[size]) { std::copy(values.begin(), values.end(), data.get()); } OwnContainer::iterator OwnContainer::begin() { return iterator(data.get()); } OwnContainer::iterator OwnContainer::end() { return iterator(data.get() + size); } OwnContainer::const_iterator OwnContainer::begin() const { return const_iterator(data.get()); } OwnContainer::const_iterator OwnContainer::end() const { return const_iterator(data.get() + size); }
      
      









当然、イテレータとconst_iteratorを同じ型のエイリアスとして定義することを妨げるものは何もありません。



イテレータ



イテレータは、std ::イテレータクラスから継承する必要があります。 このクラス自体はメソッドを実装しませんが、標準アルゴリズムで使用される必要な型を宣言します。

std ::イテレータ
g ++ 4.9での定義方法

  template<typename _Category, typename _Tp, typename _Distance = ptrdiff_t, typename _Pointer = _Tp*, typename _Reference = _Tp&> struct iterator { /// One of the @link iterator_tags tag types@endlink. typedef _Category iterator_category; /// The type "pointed to" by the iterator. typedef _Tp value_type; /// Distance between iterators is represented as this type. typedef _Distance difference_type; /// This type represents a pointer-to-value_type. typedef _Pointer pointer; /// This type represents a reference-to-value_type. typedef _Reference reference; };
      
      









これはテンプレートクラスです。テンプレートの最初のパラメータはイテレータタイプです。標準ライブラリで使用するため、タイプはinput_iterator_tag、output_iterator_tag、forward_iterator_tag、bidirectional_iterator_tag、random_access_iterator_tagから選択されます。 2番目のパラメーターは、*および->演算子によって格納および返される値のタイプです。3番目のパラメーターは、イテレーター間の距離を説明できるタイプです。4番目のテンプレートパラメーターは、値へのポインターのタイプです。5番目は、値への参照のタイプです。 最初の2つのパラメーターは必須です。



最も単純なイテレータはInputIterator(input_iterator_tag)であり、インクリメントのプレフィックス形式、演算子をサポートする必要があります!=、演算子*、および演算子->(この例ではイテレータがint型に使用されているため、この場合operator->無意味)。 さらに、コンストラクターとコピーコンストラクターが必要になります。 この例では、コンテナクラスのbeginメソッドとendメソッド以外のイテレータを作成することは意図していないため、イテレータのコンストラクタはプライベートになり、コンテナクラスはフレンドリと宣言されます。 そして、==演算子を追加します。まずサポートを追加することをお勧めします!=そして==を一緒に使用し、次にBOOST_FOREACHはそれなしでは機能しません。



const_iteratorはイテレータとそれほど違いはないので、イテレータを1つのパラメータ(*および->演算子の戻り値の型)を持つテンプレートクラスとして宣言しましょう。



OwnContainerに格納されている配列要素へのポインターをコンストラクターに渡します。



 template<typename ValueType> class OwnIterator: public std::iterator<std::input_iterator_tag, ValueType> { friend class OwnContainer; private: OwnIterator(ValueType* p); public: OwnIterator(const OwnIterator &it); bool operator!=(OwnIterator const& other) const; bool operator==(OwnIterator const& other) const; //need for BOOST_FOREACH typename OwnIterator::reference operator*() const; OwnIterator& operator++(); private: ValueType* p; };
      
      







OwnIeratorメソッドの実装
 template<typename ValueType> OwnIterator<ValueType>::OwnIterator(ValueType *p) : p(p) { } template<typename ValueType> OwnIterator<ValueType>::OwnIterator(const OwnIterator& it) : p(it.p) { } template<typename ValueType> bool OwnIterator<ValueType>::operator!=(OwnIterator const& other) const { return p != other.p; } template<typename ValueType> bool OwnIterator<ValueType>::operator==(OwnIterator const& other) const { return p == other.p; } template<typename ValueType> typename OwnIterator<ValueType>::reference OwnIterator<ValueType>::operator*() const { return *p; } template<typename ValueType> OwnIterator<ValueType> &OwnIterator<ValueType>::operator++() { ++p; return *this; }
      
      









これでやめることができますが、boostライブラリにはイテレータを作成するための基本クラスがあり、それについて少し説明したいと思います。



boostから継承したイテレーター:: iterator_facade





コンテナは、イテレータとconst_iteratorによって参照される型のみが異なります。

 class OwnContainerBBI { public: typedef OwnIteratorBB<int> iterator; typedef OwnIteratorBB<const int> const_iterator; OwnContainerBBI(std::initializer_list<int> values); iterator begin(); iterator end(); const_iterator begin() const; const_iterator end() const; private: const size_t size; std::unique_ptr<int[]> data; };
      
      





OwnContainerBBIメソッドの実装
 OwnContainerBBI::OwnContainerBBI(std::initializer_list<int> values) : size(values.size()), data(new int[size]) { std::copy(values.begin(), values.end(), data.get()); } OwnContainerBBI::iterator OwnContainerBBI::begin() { return iterator(data.get()); } OwnContainerBBI::iterator OwnContainerBBI::end() { return iterator(data.get() + size); } OwnContainerBBI::const_iterator OwnContainerBBI::begin() const { return const_iterator(data.get()); } OwnContainerBBI::const_iterator OwnContainerBBI::end() const { return const_iterator(data.get() + size); }
      
      









イテレータは、ボイラープレート型boost :: iterator_facadeを継承します。 これはテンプレートクラスです。最初のパラメーターは相続人のタイプ、2番目のタイプの値、3番目のタイプの反復子です。 std ::イテレータで使用される型は、イテレータ型として使用でき、ブースト固有の型(説明ではこのオプションは古いスタイルとして指定されています)として、std ::イテレータと同じ型を使用します。 boost :: iterator_facadeは必要なメソッドを実装します:operator *、operator ++、operator->など。 しかし、それらの実装は、イテレータに実装する必要がある補助メソッド、すなわち、間接参照、等しい、増分、減分、前進、距離に基づいています。 単純な場合(私たちのような)では、等しい、インクリメント、および間接参照のみが必要です。 これらのメソッドはイテレータインターフェイスの実装に使用されるため、privatセクションに配置し、それらを使用するクラス(boost :: iterator_core_access)をフレンドとして宣言します。



 template<typename ValueType> class OwnIteratorBB: public boost::iterator_facade< OwnIteratorBB<ValueType>, ValueType, boost::single_pass_traversal_tag > { friend class OwnContainerBBI; friend class boost::iterator_core_access; private: OwnIteratorBB(ValueType* p); public: OwnIteratorBB(const OwnIteratorBB& it); private: bool equal(OwnIteratorBB const& it) const; void increment(); typename OwnIteratorBB::reference dereference() const; ValueType* p; };
      
      







OwnIteratorBBメソッドの実装
 template<typename ValueType> OwnIteratorBB<ValueType>::OwnIteratorBB(ValueType *p) : p(p) { } template<typename ValueType> OwnIteratorBB<ValueType>::OwnIteratorBB(const OwnIteratorBB &it) : p(it.p) { } template<typename ValueType> bool OwnIteratorBB<ValueType>::equal(const OwnIteratorBB &it) const { return p == it.p; } template<typename ValueType> void OwnIteratorBB<ValueType>::increment() { ++p; } template<typename ValueType> typename OwnIteratorBB<ValueType>::reference OwnIteratorBB<ValueType>::dereference() const { return *p; }
      
      









おわりに



イテレータは、コンテナなしで作成および使用でき、コンテナがまったく必要ない場合もあります。 イテレータは、他のイテレータのラッパーとして機能し、その動作を変更できます。たとえば、1つのイテレータを通じて要素を表示します。 または、データは個別に保存され、いくつかのキーまたはフィールド値を持つコンテナが個別に保存されます。 また、すべての要望を処理するイテレータを編成することもできますが、2番目のコンテナの値に基づいて特定の条件に対応するイテレータのみを返します。 k06aによって書かれた記事の過小評価されたイテレーターに、さらに多くのアイデアがあります



単純なイテレーターの場合、boost :: iterator_facadeの使用はあまり重要ではありませんが、より複雑なイテレーターの場合、コード量を削減します。もちろん、ブーストライブラリが既に使用されている場合、iterator_facadeのためだけにプルすることは意味がありません。



参照資料



1. cppreference、comのIteratorライブラリの説明

2. boostの説明:: iterator_facade

3. githubにサンプルがあるリポジトリ 。 プロジェクトはQt5(テスト用)を使用します。boostが使用されるため、boostを含むディレクトリへのパスでBOOST環境変数を宣言する必要があります。



All Articles