1. ホーム
  2. c++

[解決済み】STLスタイルのイテレータを実装し、よくある落とし穴を回避する方法は?

2022-03-25 23:12:55

質問

STLスタイルのランダムアクセス可能なイテレータを提供したいコレクションを作りました。イテレータの実装例を探していたのですが、見つかりませんでした。の const オーバーロードの必要性については知っています。 []* 演算子を使用することができます。イテレータが "STL-style"であるための要件と、(もしあれば)避けるべき他の落とし穴は何でしょうか?

追加の文脈 これはあるライブラリのためのもので、本当に必要でない限り、それへの依存性を導入したくありません。私は、同じコンパイラでC++03とC++11の間のバイナリ互換性を提供できるように、独自のコレクションを書いています(したがって、おそらく壊れるであろうSTLはありません)。

どのように解決するのですか?

http://www.cplusplus.com/reference/std/iterator/ には、C++11 標準の§24.2.2 の仕様を詳しく説明した便利なチャートがあります。 基本的に、イテレータには有効な操作を記述するタグがあり、そのタグには階層がある。 以下は純粋にシンボリックなもので、これらのクラスは実際には存在しない。

iterator {
    iterator(const iterator&);
    ~iterator();
    iterator& operator=(const iterator&);
    iterator& operator++(); //prefix increment
    reference operator*() const;
    friend void swap(iterator& lhs, iterator& rhs); //C++11 I think
};

input_iterator : public virtual iterator {
    iterator operator++(int); //postfix increment
    value_type operator*() const;
    pointer operator->() const;
    friend bool operator==(const iterator&, const iterator&);
    friend bool operator!=(const iterator&, const iterator&); 
};
//once an input iterator has been dereferenced, it is 
//undefined to dereference one before that.

output_iterator : public virtual iterator {
    reference operator*() const;
    iterator operator++(int); //postfix increment
};
//dereferences may only be on the left side of an assignment
//once an output iterator has been dereferenced, it is 
//undefined to dereference one before that.

forward_iterator : input_iterator, output_iterator {
    forward_iterator();
};
//multiple passes allowed

bidirectional_iterator : forward_iterator {
    iterator& operator--(); //prefix decrement
    iterator operator--(int); //postfix decrement
};

random_access_iterator : bidirectional_iterator {
    friend bool operator<(const iterator&, const iterator&);
    friend bool operator>(const iterator&, const iterator&);
    friend bool operator<=(const iterator&, const iterator&);
    friend bool operator>=(const iterator&, const iterator&);

    iterator& operator+=(size_type);
    friend iterator operator+(const iterator&, size_type);
    friend iterator operator+(size_type, const iterator&);
    iterator& operator-=(size_type);  
    friend iterator operator-(const iterator&, size_type);
    friend difference_type operator-(iterator, iterator);

    reference operator[](size_type) const;
};

contiguous_iterator : random_access_iterator { //C++17
}; //elements are stored contiguously in memory.

を特殊化することができます。 std::iterator_traits<youriterator> を継承するか、イテレータ自体に同じ型付けをするか、あるいは std::iterator (これらの typedef を持つ)。 私は2番目の選択肢を選びます. std 名前空間と読みやすさのためですが、ほとんどの人が std::iterator .

struct std::iterator_traits<youriterator> {        
    typedef ???? difference_type; //almost always ptrdiff_t
    typedef ???? value_type; //almost always T
    typedef ???? reference; //almost always T& or const T&
    typedef ???? pointer; //almost always T* or const T*
    typedef ???? iterator_category;  //usually std::forward_iterator_tag or similar
};

iterator_category は以下のいずれかでなければならないことに注意してください。 std::input_iterator_tag , std::output_iterator_tag , std::forward_iterator_tag , std::bidirectional_iterator_tag または std::random_access_iterator_tag イテレータがどのような要件を満たすかによって異なります。 イテレータの種類によっては std::next , std::prev , std::advance および std::distance もありますが、これはほとんど必要ありません。 で 極稀に に特化させたい場合があります。 std::beginstd::end .

コンテナには、おそらく const_iterator これは、定数データへの(おそらくミュータブルな)イテレータで iterator を暗黙のうちに構成可能であることを除いては。 iterator であり、ユーザがデータを変更できないようにする必要があります。 その内部ポインタは定数でないデータへのポインタであることが一般的であり,また iterator を継承しています。 const_iterator のように、コードの重複を最小限に抑えることができます。

での私の投稿は STLコンテナを自作する には、より完全なコンテナ/イテレータのプロトタイプがあります。