web-dev-qa-db-ja.com

「!」が「<」の代わりにイテレータで使用されるのはなぜですか?

私はこのようなループを書くことに慣れています:

_for (std::size_t index = 0; index < foo.size(); index++)
{
    // Do stuff with foo[index].
}
_

しかし、他人のコードでイテレータループを見ると、次のようになります。

_for (Foo::Iterator iterator = foo.begin(); iterator != foo.end(); iterator++)
{
    // Do stuff with *Iterator.
}
_

私はiterator != foo.end()が不快だと思います。 iteratorが1つ以上増加する場合も危険です。

iterator < foo.end()を使用する方が「正しい」ように見えますが、実際のコードではそれを見たことはありません。何故なの?

51
Maxpm

すべての反復子は同等であると同等です。ランダムアクセスの反復子のみが、相対的に比較可能です。入力反復子、前方反復子、および双方向反復子は、相対的に比較できません。

したがって、_!=_を使用した比較は、_<_を使用した比較よりも汎用的で柔軟です。


要素のすべての範囲が同じアクセスプロパティを持つわけではないため、イテレータにはさまざまなカテゴリがあります。例えば、

  • 配列(要素の連続したシーケンス)へのイテレータがある場合、それらを関係的に比較することは簡単です。イテレータには要素へのポインタが含まれている可能性が高いため、ポイントされている要素のインデックス(またはそれらへのポインタ)を比較するだけです。

  • リンクリストへのイテレータがあり、1つのイテレータが別のイテレータより「少ない」かどうかをテストする場合は、リンクリストのノードを1つのイテレータから他のイテレータに到達するか、最後に到達するまでウォークする必要があります。リストの。

ルールは、イテレータのすべての操作は、一定の時間の複雑さ(または、少なくとも、準線形の時間の複雑さ)を持つべきであるということです。イテレータが同じオブジェクトを指しているかどうかを比較する必要があるだけなので、常に等しい時間で等値比較を実行できます。したがって、すべてのイテレータは同等であると同等です。


さらに、反復子が指す範囲の終わりを超えて反復子をインクリメントすることはできません。したがって、it != foo.end()it < foo.end()と同じことを行わないシナリオに陥った場合、範囲の終わりを超えて反復したため、未定義の動作が既にあります。

配列へのポインターについても同じことが言えます。配列の最後の1つを超えてポインターをインクリメントすることはできません。これを行うプログラムは、未定義の動作を示します。 (インデックスは単なる整数なので、同じことはインデックスには明らかに当てはまりません。)

一部の標準ライブラリの実装(Visual C++標準ライブラリの実装など)には、このようなイテレータで何か違法なことをするとアサーションを発生させる有用なデバッグコードがあります。

73
James McNellis

短い答え:Iteratorは数値ではないので、オブジェクトです。

より長い答え:線形配列よりも多くのコレクションがあります。たとえば、ツリーとハッシュは、「このインデックスはこの他のインデックスの前にあります」にはあまり向いていません。たとえば、ツリーの場合、別々のブランチに存在する2つのインデックス。または、ハッシュ内の任意の2つのインデックス-それらにはまったく順序がないため、それらに課す順序は任意です。

「欠けている」End()について心配する必要はありません。これも数字ではなく、コレクションの終わりを表すオブジェクトです。イテレータを通過させることは意味をなさず、実際に通過することはできません。

11
Mike Caron