回答:
const_iterator
sでは、それらが指す値を変更することはできませiterator
ん。通常のs では変更できます。
C ++のすべてのものと同様に、const
通常のイテレータを使用する正当な理由がない限り(つまりconst
、ポイントされた値を変更しないという事実を使用したい場合)、常にを優先します。
彼らはほとんど自明でなければなりません。イテレータがタイプTの要素を指す場合、const_iteratorはタイプ 'const T'の要素を指します。
基本的にはポインタ型と同等です:
T* // A non-const iterator to a non-const element. Corresponds to std::vector<T>::iterator
T* const // A const iterator to a non-const element. Corresponds to const std::vector<T>::iterator
const T* // A non-const iterator to a const element. Corresponds to std::vector<T>::const_iterator
constイテレータは常に同じ要素を指すため、イテレータ自体はconstです。ただし、ポイントする要素はconstである必要はないため、ポイントする要素は変更できます。const_iteratorはconst要素を指すイテレータであるため、イテレータ自体を更新(インクリメントまたはデクリメントなど)することはできますが、ポイントする要素は変更できません。
const iterater
との間の微妙さを説明するために賛成const_iterator
。
不幸なことに、STLコンテナーの多くのメソッドは、パラメーターとしてconst_iteratorsではなくイテレーターを使用します。したがって、const_iteratorがある場合、「このイテレータが指す要素の前に要素を挿入する」とは言えません(このようなことは概念的にはconst違反ではないと私は考えています)。とにかくそれをしたい場合は、std :: advance()またはboost :: next()を使用して非constイテレータに変換する必要があります。例えば。boost :: next(container.begin()、std :: distance(container.begin()、the_const_iterator_we_want_to_unconst))。コンテナーがstd :: listの場合、その呼び出しの実行時間はO(n)になります。
つまり、constを「論理的」な場所に追加するという普遍的なルールは、STLコンテナーに関してはそれほど普遍的ではありません。
ただし、boostコンテナはconst_iteratorsを使用します(例:boost :: unordered_map :: erase())。したがって、ブーストコンテナーを使用すると、「const agressive」になることができます。ところで、STLコンテナーが修正されるかどうか、いつ修正されるかは誰にもわかりますか?
vector
and の場合、deque
1つの要素を挿入すると、既存のすべてのイテレータが無効になりますconst
。しかし、私はあなたの主張を理解しています。このような操作はconst
、イテレーターではなくコンテナー性によって保護されます。また、標準のコンテナーインターフェイスにconstからnonconstの反復子変換関数がないのはなぜでしょうか。
int const * foo;
int * const foo;
、int const * const foo;
3つすべてが有効であり、それぞれ独自の方法で有効であると言えます。 std::vector<int> const bar
2番目のものと同じである必要がありますが、残念ながら3番目のように扱われることがよくあります。問題の根本的な原因は、ベクトルの場合std::vector<int const> bar;
と同じ効果を得る方法がない場合に、いつだかわかりませんint const *foo;
。
最小限の実行可能な例
非constイテレータを使用すると、ポイントする対象を変更できます。
std::vector<int> v{0};
std::vector<int>::iterator it = v.begin();
*it = 1;
assert(v[0] == 1);
定数イテレータはしません:
const std::vector<int> v{0};
std::vector<int>::const_iterator cit = v.begin();
// Compile time error: cannot modify container with const_iterator.
//*cit = 1;
上記のように、v.begin()
はconst
オーバーロードされ、コンテナ変数の定数に応じて、iterator
またはconst_iterator
そのどちらかに応じて返されます。
const_iterator
ポップアップの一般的なケースはthis
、const
メソッド内で使用される場合です。
class C {
public:
std::vector<int> v;
void f() const {
std::vector<int>::const_iterator it = this->v.begin();
}
void g(std::vector<int>::const_iterator& it) {}
};
const
なりthis
ますこれは、constのthis->v
constの。
通常はでそれを忘れることができますが、auto
これらのイテレータを渡し始める場合は、メソッドシグネチャについてそれらについて考える必要があります。
constおよびnon-constと同様に、非constからconstに簡単に変換できますが、その逆はできません。
std::vector<int> v{0};
std::vector<int>::iterator it = v.begin();
// non-const to const.
std::vector<int>::const_iterator cit = it;
// Compile time error: cannot modify container with const_iterator.
//*cit = 1;
// Compile time error: no conversion from const to no-const.
//it = ci1;
どちらを使用するか:const int
vsに類似int
:コンテナを変更する必要がない場合は常に使用するconstイテレータを使用して、変更せずに読み取る意図をより適切に文書化します。
ok最初に非常に簡単な例で説明します。定数イテレータを使用せずに、ランダムな整数のコレクション「randomData」があると考えてください。
for(vector<int>::iterator i = randomData.begin() ; i != randomData.end() ; ++i)*i = 0;
for(vector<int>::const_iterator i = randomData.begin() ; i!= randomData.end() ; ++i)cout << *i;
コレクション内でのデータの書き込み/編集に見られるように、通常のイテレータが使用されていますが、読み取り目的には定数イテレータが使用されています。最初のforループで定数反復子を使用しようとすると、エラーが発生します。経験則として、コレクション内のデータを読み取るには定数イテレータを使用します。