序章
ポインターの代わりにイテレーターを使用することには多くのメリットがあります。
- releaseとdebugで異なるコードパス、および;
- より良い型安全性、および;
- ジェネリック コードを記述できるようにする (反復子は、リンク リストなどの任意のデータ構造で動作するように作成できますが、組み込みポインターはこの点で非常に制限されています)。
デバッグ
とりわけ、範囲の終わりを渡されたイテレータの逆参照はundefined-behaviorであるため、実装は、そのような場合に必要と思われることを自由に行うことができます。
gccが提供する標準ライブラリの実装であるlibstdc++は、何らかの障害を検出すると診断を発行します (デバッグ モードが有効な場合)。
例
#define _GLIBCXX_DEBUG 1 /* enable debug mode */
#include <vector>
#include <iostream>
int
main (int argc, char *argv[])
{
std::vector<int> v1 {1,2,3};
for (auto it = v1.begin (); ; ++it)
std::cout << *it;
}
/usr/include/c++/4.9.2/debug/safe_iterator.h:261:error: attempt to
dereference a past-the-end iterator.
Objects involved in the operation:
iterator "this" @ 0x0x7fff828696e0 {
type = N11__gnu_debug14_Safe_iteratorIN9__gnu_cxx17__normal_iteratorIPiNSt9__cxx19986vectorIiSaIiEEEEENSt7__debug6vectorIiS6_EEEE (mutable iterator);
state = past-the-end;
references sequence with type `NSt7__debug6vectorIiSaIiEEE' @ 0x0x7fff82869710
}
123
デバッグモードであるかどうかに関係なく、ポインターを使用している場合、上記は発生しません。
libstdc++のデバッグ モードを有効にしない場合、よりパフォーマンスに適したバージョン (ブックキーピングを追加しない) の実装が使用され、診断は発行されません。
(潜在的に) より良い型安全性
イテレータの実際の型はimplementation-definedであるため、これを使用して型安全性を高めることができますが、実装のドキュメントをチェックして、これが当てはまるかどうかを確認する必要があります。
以下の例を考えてみましょう:
#include <vector>
struct A { };
struct B : A { };
// .-- oops
// v
void it_func (std::vector<B>::iterator beg, std::vector<A>::iterator end);
void ptr_func (B * beg, A * end);
// ^-- oops
int
main (int argc, char *argv[])
{
std::vector<B> v1;
it_func (v1.begin (), v1.end ()); // (A)
ptr_func (v1.data (), v1.data () + v1.size ()); // (B)
}
推敲
- (A)実装によっては、同じ型ではない可能性があるため
std::vector<A>::iterator
、コンパイル時エラーになる可能性があります。std::vector<B>::iterator
- (B)
B*
ただし、 からへの暗黙的な変換があるため、常にコンパイルされA*
ます。