6

注: この質問は にも当てはまりeraseます。下を参照してください。


で呼び出されたend() - 1後にイテレータが無効化されるという事実の背後にある理由は何ですか?pop_backvector

明確にするために、私はこの状況について言及しています:

std::vector<int> v;
v.push_back(1);
v.push_back(2);

std::vector<int>::iterator i1 = v.begin(), i2 = v.end() - 1, i3 = v.begin() + 1;

v.pop_back();

// i1 is still valid
// i2 is now invalid
// i3 is now invalid too

std::vector<int>::iterator i4 = v.end();

assert(i2 == i4);  // undefined behavior (but why should it be?!)
assert(i3 == i4);  // undefined behavior (but why should it be?!)

なぜこれが起こるのですか?(つまり、この無効化が実装にとって有益であることが判明するのはいつですか?)

(これは単なる理論上の問題ではないことに注意してください。Visual C++ 2013 (おそらく 2012 でも) に_ITERATOR_DEBUG_LEVEL設定している場合、デバッグ モードでこれを実行しようとするとエラーが表示され2ます。)


についてerase

同じ質問が に も当てはまることに注意してくださいerase:
erase(end() - 1, end())end() - 1

(ですから、 「を呼び出すのと同じだから無効になるpop_backend() - 1erase(end() - 1, end())とは言わないでください。それはただの質問です。)

4

2 に答える 2

0

pop_backは通常、 の観点から定義されerase(end()-1, end())ます。

ベクターから反復子の範囲を消去すると、最初に消去された以降のすべての反復子が無効になります。これには 1 の範囲が含まれます。

一般に、有効な derefencable 非入力反復子は、無効になるまで常に同じデータの「場所」を参照します。の場合、eraseその後のすべての無効でないイテレータは現在、同じ値場所を持ちます。

上記のルールは両方とも、必要な動作を得るために修正する必要があります。最初は、「最後の要素を消去しない限り、その場合、最初に消去された要素が最後の 1 つ後ろのイテレータになる」のようなものです。そして、まだ有効なイテレータは逆参照できなくなります。これは、私の知る限り、C++ では前例のないイテレータのおそらく固有の状態変化です。

コストは、要求された動作をカバーするための標準の余分なトリッキーな言葉遣いと、厳密なイテレータのサニティ チェックの両方です。利点 -- まあ、私にはわかりません: あらゆる状況で、何が起こったのかを正確に知る必要があります (非常に特定のイテレータが、無効化されるのではなく、最後を過ぎて 1 つになっただけです)。について話すことができendます。

そして言葉が必要です。を呼び出すとerase( a, b )、以降のすべてのイテレータaの状態が何らかの方法で変更されます (*a同じ値を返すわけではなく、その変更方法を指定する必要があります)。C++ は簡単な方法を取り、状態が変更されるすべてのイテレータeraseが無効になると単純に述べ、それを使用すると未定義の動作になります。これにより、実装者に最大限の自由度が与えられます。

理論的には、最適化も可能です。操作の前後で反復子を逆参照すると、erase取得する値は同じと見なすことができます。(オブジェクト デストラクタ内でコンテナを間接的に変更する可能性はないと仮定しますが、これも同様に証明できます)。

于 2013-07-22T03:47:10.437 に答える