イテレーターの無効化ルール


543

C ++コンテナーのイテレーター無効化ルールとは何ですか?

できれば要約リスト形式で。

(注:これはStack OverflowのC ++ FAQへのエントリになることを目的としています。このフォームでFAQを提供するという考えを批評したい場合は、これをすべて開始したメタへの投稿がそのための場所になります。回答その質問はC ++チャットルームで監視され、FAQのアイデアはそもそも最初から始まっているため、アイデアを思いついた人があなたの答えを読む可能性は非常に高いです。


回答はあなたの回答と同じ形式である必要がありますか?
PW

@PWはIMOそれは対称性のために好ましいであろうが、私はそれを強制することはできません:P
軌道上での明度レース

c ++ 20はどうですか?
Walter、

1
@Walterはまだ存在しません;)
オービットでの軽さのレース

この質問は、愚かな時代からのフューチュラマからのリーラを引用するためのものであり、私の控えめな意見では開いたままにしておくべきです。
RomanLuštrik19年

回答:


112

C ++ 17(すべての参照はCPP17- n4659の最終草案からのものです


挿入

シーケンスコンテナー

  • vector:関数insertemplace_backemplacepush_back原因の再割り当て新しいサイズが古い容量よりも大きい場合。再割り当ては、シーケンス内の要素を参照するすべての参照、ポインター、イテレーターを無効にします。再割り当てが発生しない場合、挿入ポイントの前のすべてのイテレータと参照は有効なままです。[26.3.11.5/1] 関数
    に関してreserve、再割り当ては、シーケンス内の要素を参照するすべての参照、ポインター、イテレーターを無効にします。の呼び出し後reserve()の挿入中は、挿入によってベクトルのサイズがの値よりも大きくなるまで、再割り当ては行われませんcapacity()。[26.3.11.3/6]

  • deque:両端キューの途中に挿入すると、すべての反復子と両端キューの要素への参照が無効になります。両端キューの両端に挿入すると、両端キューへのすべてのイテレータが無効になりますが、両端キューの要素への参照の有効性には影響しません。[26.3.8.4/1]

  • list:イテレータと参照の有効性には影響しません。例外がスローされても影響はありません。[26.3.10.4/1]。、、、、、の機能は、この規則の下で覆われています。
    insertemplace_frontemplace_backemplacepush_frontpush_back

  • forward_list:のオーバーロードinsert_afterはイテレータと参照の有効性に影響を与えません[26.3.9.5/1]

  • array原則として、配列のイテレータは、配列の有効期間を通じて無効になることはありません。ただし、スワップ中、イテレータは同じ配列要素をポイントし続けるため、その値が変更されることに注意してください。

連想コンテナ

  • All Associative Containersinsertおよびemplaceメンバーは、イテレータの有効性とコンテナへの参照に影響を与えてはなりません[26.2.6 / 9]

順不同の連想コンテナ

  • All Unordered Associative Containers:ハッシュを変更すると、イテレータが無効になり、要素間の順序が変更され、要素が表示されるバケットが変更されますが、要素へのポインタや参照は無効になりません。[/ 9 26.2.7] とメンバーは、コンテナ要素への参照の有効性に影響を与えないものとしますが、コンテナにすべてのイテレータを無効にすることができます。[26.2.7 / 14] ザ及び場合メンバーはイテレータの有効性に影響を与えてはならない場合、挿入操作の前に、容器内の要素の数であり、挿入された要素の数は、あるコンテナのバケット数であり、そしてありますコンテナの最大負荷係数。[26.2.7 / 15]
    insertemplace
    insertemplace(N+n) <= z * BNnBz

  • All Unordered Associative Containers:マージ操作(例a.merge(a2):)の場合、転送された要素を参照する反復子と参照するすべての反復子aは無効になりますが、残っている要素への反復子a2は有効のままです。(表91 —順不同の連想コンテナの要件)

コンテナアダプター

  • stack:基になるコンテナーから継承
  • queue:基になるコンテナーから継承
  • priority_queue:基になるコンテナーから継承

消去

シーケンスコンテナー

  • vector:関数erasepop_back、消去の時点以降のイテレータと参照を無効にします。[26.3.11.5/3]

  • deque:の最後の要素を消去する消去操作はdeque、過去の終わりのイテレータとすべてのイテレータと消去された要素への参照のみを無効にします。aの最初の要素を消去しdeque、最後の要素は消去しない消去操作では、イテレータと消去された要素への参照のみが無効になります。の最初の要素も最後の要素も消去しない消去操作はdeque、過去の終わりのイテレータとすべてのイテレータを無効にし、のすべての要素への参照を無効にしdequeます。[注:pop_frontおよびpop_backは消去操作です。-注の終了] [26.3.8.4/4]

  • list:イテレータと消去された要素への参照のみを無効にします。[26.3.10.4/3]。これが適用されるerasepop_frontpop_backclear機能しています。
    removeremove_ifメンバー関数:リスト反復子によって参照されるリスト内のすべての要素を消去します。このi条件については、次の条件が満たされます:*i == valuepred(*i) != false。イテレータと消去された要素への参照のみを無効にします[26.3.10.5/15]。
    uniqueメンバ関数-消去し、全てが、同じ要素のすべての連続するグループからの最初の要素は、イテレータによって参照i範囲内に[first + 1, last)いる*i == *(i-1)(引数なしで一意のバージョンの場合)またはpred(*i, *(i - 1))(述語引数を持つ一意のバージョンの場合)が保持されます。イテレータと消去された要素への参照のみを無効にします。[26.3.10.5/19]

  • forward_listerase_afterイテレータと消去された要素への参照のみを無効にします。[26.3.9.5/1]。
    removeremove_ifメンバー関数-リスト反復子iによって参照されるリスト内のすべての要素を消去します。この条件*i == valueについてremove()、(for )、pred(*i)true(for remove_if())の条件が満たされます。イテレータと消去された要素への参照のみを無効にします。[26.3.9.6/12]。
    uniqueメンバー関数-[first + 1、last)の範囲でイテレーターiによって参照される等しい要素のすべての連続するグループから最初の要素を除くすべてを削除します*i == *(i-1)(引数のないバージョンの場合)またはpred(*i, *(i - 1))(述語のあるバージョンの場合)引数)が成り立つ。イテレータと消去された要素への参照のみを無効にします。[26.3.9.6/16]

  • All Sequence Containersclearaの要素を参照するすべての参照、ポインター、イテレーターを無効にし、最後を過ぎたイテレーターを無効にする場合があります(表87-シーケンスコンテナーの要件)。しかし、forward_listでは、clear過去のイテレータを無効にしません。[26.3.9.5/32]

  • All Sequence Containersassignコンテナの要素を参照するすべての参照、ポインタ、イテレータを無効にします。以下のためにvectordeque、また過去エンドイテレータを無効にします。(表87-シーケンスコンテナーの要件)

連想コンテナ

  • All Associative Containerseraseメンバーは、イテレーターと消去された要素への参照のみを無効にする必要があります[26.2.6 / 9]

  • All Associative Containersextractメンバーは、削除された要素への反復子のみを無効にします。削除された要素へのポインタと参照は引き続き有効です[26.2.6 / 10]

コンテナアダプター

  • stack:基になるコンテナーから継承
  • queue:基になるコンテナーから継承
  • priority_queue:基になるコンテナーから継承

イテレーターの無効化に関連する一般的なコンテナー要件:

  • 特に明記されていない限り(明示的に、または他の関数で関数を定義して)、コンテナーメンバー関数を呼び出したり、コンテナーをライブラリ関数の引数として渡したりしても、そのコンテナー内のオブジェクトに対するイテレーターが無効になったり、オブジェクトの値が変更されたりすることはありません。[26.2.1 / 12]

  • swap()スワップされるコンテナーの要素を参照する参照、ポインター、イテレーターを無効にする関数はありません。[注:end()イテレータは要素を参照しないため、無効になる可能性があります。-終了ノート] [26.2.1 /(11.6)]

上記の要件の例として:

  • transformアルゴリズム:opand binary_op関数は、イテレーターまたはサブ範囲を無効にしたり、範囲内の要素を変更したりしてはなりません[28.6.4 / 1]

  • accumulateアルゴリズム:[first、last]の範囲でbinary_opは、要素を変更したり、イテレータやサブ範囲を無効にしたりしない[29.8.2 / 1]

  • reduceアルゴリズム:binary_opは、イテレータまたはサブ範囲を無効にしたり、範囲[最初、最後]の要素を変更したりしません。[29.8.3 / 5]

等々...


7
ああPWあなたヒーロー!
Orbitのライトネスレース

2
@LightnessRacesinOrbit:元の回答形式に従って実行しようとしました。:)
PW

1
のリストも入手できstd::stringますか?std::vectorSSOによるものとは異なると思います
sp2danny

1
@ sp2danny:SSOが原因で、string上記の2番目の一般的な要件が満たされません。だから私はそれを含めなかった。また、以前のFAQエントリと同じパターンを維持しようとしました。
PW

@LightnessRaceswithMonica頑張ってくれてありがとう。何日も混乱させられる質問があります。これらのコンテキストで「無効」とはどういう意味ですか?それはどういう意味"invalidated" can mean "no longer points to what it used to", not just "may not point to any valid element"@Marshallクロウが、この中で説明したような答え?または、2つの条件のうち1つだけを示していますか?
Rick

410

C ++ 03(ソース:イテレーター無効化ルール(C ++ 03)


挿入

シーケンスコンテナー

  • vector:新しいコンテナのサイズが以前の容量より大きい場合を除いて、挿入点より前のすべてのイテレータと参照は影響を受けません(その場合、すべてのイテレータと参照は無効になります)[23.2.4.3/1]
  • deque:挿入されたメンバーが両端キューの最後(前または後ろ)にない限り、すべての反復子と参照が無効になります(この場合、すべての反復子は無効になりますが、要素への参照は影響を受けません)[23.2.1.3/1]
  • list:すべてのイテレータと参照は影響を受けない[23.2.2.3/1]

連想コンテナ

  • [multi]{set,map}:すべてのイテレータと参照は影響を受けません[23.1.2 / 8]

コンテナアダプター

  • stack:基になるコンテナーから継承
  • queue:基になるコンテナーから継承
  • priority_queue:基になるコンテナーから継承

消去

シーケンスコンテナー

  • vector:消去ポイント以降のすべてのイテレータと参照が無効化されている[23.2.4.3/3]
  • deque:消去されたメンバーが両端キューの最後(前または後ろ)にない限り、すべてのイテレーターと参照が無効になります(この場合、消去されたメンバーへのイテレーターと参照のみが無効になります)[23.2.1.3/4]
  • list:イテレータと消去された要素への参照のみが無効化されます[23.2.2.3/3]

連想コンテナ

  • [multi]{set,map}:イテレータと消去された要素への参照のみが無効になります[23.1.2 / 8]

コンテナアダプター

  • stack:基になるコンテナーから継承
  • queue:基になるコンテナーから継承
  • priority_queue:基になるコンテナーから継承

サイズ変更

  • vector:挿入/消去による[23.2.4.2/6]
  • deque:挿入/消去による[23.2.1.2/1]
  • list:挿入/消去による[23.2.2.2/1]

注1

特に明記されていない限り(明示的に、または他の関数で関数を定義して)、コンテナーメンバー関数を呼び出したり、コンテナーをライブラリー関数の引数として渡したりしても、そのコンテナー内のオブジェクトに対するイテレーター無効になったり、オブジェクトの値が変更されたりすることはありません。[23.1 / 11]

注2

C ++ 2003では、「終了」イテレータが上記の規則に従うかどうかは明確ではありません。とにかく、それらはそうであると想定する必要があります(これは実際のケースです)。

注3

ポインターの無効化の規則は、参照の無効化の規則と同じです。


5
良い考えですが、連想させるコンテナは単一の行にまとめることができると思います。そして、順序付けられていない連想配列の別の行を追加する価値があると思います...しかし、リハッシュ部分がどのようになるかはわかりません挿入/消去にマッピングされていますが、リハッシュがトリガーされるかどうかを確認する方法を知っていますか?
Matthieu M.11年

1
IIRC、仕様のどこかで、終了イテレータは「そのコンテナ内のオブジェクトへの」イテレータではないと述べています。それらの保証がそれぞれの場合にどのように終了イテレータを探すのでしょうか?
Johannes Schaub-litb

1
@MuhammadAnnaqeeb:私が近道をしたので、この答えは確かにそれを明確にしませんが、意図は、再割り当てが必要であるかのように、サイズ変更挿入/消去であると言うことです、あなたはそれを消去と同じであると考えるかもしれません次に、影響を受けるすべての要素を再挿入します。答えのそのセクションは確かに改善される可能性があります。
オービットのライトネスレース

1
@ヤク:しかし、そうではありません。引用されている標準テキストを参照してください。C ++ 11で修正されたようです。:)
オービットのライトネスレース

1
@metamorphosis:dequeはデータを隣接しないブロックに格納します。先頭または末尾に挿入すると、新しいブロックが割り当てられますが、前の要素を移動することはないため、ポインターは有効なままです。ただし、新しいブロックが割り当てられると、次の要素または前の要素に移動するためのルールが変わるため、イテレータは無効になります。
Nick Matteo

357

C ++ 11(ソース:イテレーター無効化ルール(C ++ 0x)


挿入

シーケンスコンテナー

  • vector:新しいコンテナーのサイズが以前の容量よりも大きい場合を除いて、挿入点より前のすべてのイテレーターと参照は影響を受けません(その場合、すべてのイテレーターと参照は無効になります)[23.3.6.5/1]
  • deque:挿入されたメンバーが両端キューの最後(前または後ろ)にない限り、すべての反復子と参照が無効になります(この場合、すべての反復子は無効になりますが、要素への参照は影響を受けません)[23.3.3.4/1]
  • list:すべてのイテレータと参照は影響を受けません[23.3.5.4/1]
  • forward_list:すべてのイテレータと参照は影響を受けません(に適用insert_after [23.3.4.5/1]
  • array:(なし

連想コンテナ

  • [multi]{set,map}:すべてのイテレータと参照は影響を受けません[23.2.4 / 9]

ソートされていない連想コンテナ

  • unordered_[multi]{set,map}:再ハッシュが発生するとすべての反復子が無効になりますが、参照は影響を受けません[23.2.5 / 8]。挿入がコンテナのサイズを超えてはならない場合焼き直しが発生していないz * Bところz、最大負荷率とでBバケットの現在の数が。[23.2.5 / 14]

コンテナアダプター

  • stack:基になるコンテナーから継承
  • queue:基になるコンテナーから継承
  • priority_queue:基になるコンテナーから継承

消去

シーケンスコンテナー

  • vector:消去ポイント以降のすべてのイテレータと参照が無効化される[23.3.6.5/3]
  • deque:最後の要素を消去すると、イテレータと、消去された要素への参照と最後を過ぎたイテレータのみが無効になります。最初の要素を消去すると、イテレータと消去された要素への参照のみが無効になります。他の要素を消去すると、すべての反復子と参照(過去の反復子を含む)が無効になります[23.3.3.4/4]
  • list:イテレータと消去された要素への参照のみが無効になります[23.3.5.4/3]
  • forward_list:イテレータと消去された要素への参照のみが無効化されます(に適用erase_after [23.3.4.5/1]
  • array:(なし

連想コンテナ

  • [multi]{set,map}:イテレータと消去された要素への参照のみが無効になります[23.2.4 / 9]

順不同の連想コンテナ

  • unordered_[multi]{set,map}:イテレータと消去された要素への参照のみが無効になります[23.2.5 / 13]

コンテナアダプター

  • stack:基になるコンテナーから継承
  • queue:基になるコンテナーから継承
  • priority_queue:基になるコンテナーから継承

サイズ変更

  • vector:挿入/消去ごと[23.3.6.5/12]
  • deque:挿入/消去ごと[23.3.3.3/3]
  • list:挿入/消去による[23.3.5.3/1]
  • forward_list:挿入/消去ごと[23.3.4.5/25]
  • array:(なし)

注1

特に明記されていない限り(明示的に、または他の関数で関数を定義して)、コンテナーメンバー関数を呼び出したり、コンテナーをライブラリー関数の引数として渡したりしても、そのコンテナー内のオブジェクトに対するイテレーター無効になったり、オブジェクトの値が変更されたりすることはありません。[23.2.1 / 11]

注2

swap()関数 は、スワップされるコンテナーの要素を参照する参照、ポインター、またはイテレーター無効にしません。[注:end()イテレータは要素を参照しないため、無効になる可能性があります。-終了ノート] [23.2.1 / 10]

注3

上記の注意点について以外にswap()それは「終わり」のイテレータは、上記コンテナーごとのルールの対象となるかどうかは明らかではありません。とにかく、彼らはそうであると仮定する必要があります。

注4

vectorそして、すべての順序付けreserve(n)されていない連想コンテナのサポートにより、少なくともコンテナのサイズがになるまで、自動サイズ変更が発生しないことが保証されnます。将来の提案では最小負荷係数を指定できるようになるため、順序付けされていない連想コンテナには注意が必要です。これにより、insert十分なerase操作によってコンテナサイズが最小値よりも小さくなった後、再ハッシュが発生します。保証は、後に無効になる可能性があると見なされますerase


のほかにswap()、コピー/移動割り当て時のイテレータの有効性のルールは何ですか?
goodbyeera 2014年

@LightnessRacesinOrbit:挿入、消去、サイズ変更、スワップと同様に、コピー/移動の割り当てもstd :: vectorのメンバー関数であるため、イテレーターの有効性のルールも提供できると思います。
さようなら2014年

@goodbyeera:要素の割り当てをコピー/移動するということですか?これはイテレータには影響しません。なぜでしょうか?あなたはヒットしている(注1)を上に。
2014

1
std::basic_stringはコンテナとして数えられていないようで、確かに注記が適用される規格のセクションのコンテナではないので、私は間違いを犯したと思います。それでも、SSOが許可されていないのはどこですか(COWは知っています)。
Deduplicator

2
これらのルールはC ++ 14でも同じですか?C ++ 17(現在知られている限り)?
einpoklum 2016年

40

それはどのような種類の挿入反復子は(と付け加えおそらく価値があるstd::back_insert_iteratorstd::front_insert_iteratorstd::insert_iteratorすべての挿入は、このイテレータを介して実行され、他の独立したイテレータ・無効イベントが発生しないよう)限り有効のままに保証されています。

たとえば、std::vectorを使用しstd::insert_iteratorてに一連の挿入操作を実行している場合、これらの挿入によってベクターの再割り当てがトリガーされ、そのベクターを「指す」すべての反復子が無効になる可能性があります。ただし、問題の挿入反復子は有効なままであることが保証されています。つまり、挿入のシーケンスを安全に続行できます。ベクトルの再割り当てのトリガーについて心配する必要はまったくありません。

これも、挿入反復子自体を介して実行される挿入にのみ適用されます。イテレーター無効化イベントがコンテナーに対する何らかの独立したアクションによってトリガーされた場合、挿入イテレーターも一般的な規則に従って無効になります。

たとえば、このコード

std::vector<int> v(10);
std::vector<int>::iterator it = v.begin() + 5;
std::insert_iterator<std::vector<int> > it_ins(v, it);

for (unsigned n = 20; n > 0; --n)
  *it_ins++ = rand();

ベクトルがこのプロセスの途中で再割り当てを「決定」した場合でも、ベクトルへの有効な挿入シーケンスの実行が保証されます。イテレータitは明らかに無効になりますが、it_ins引き続き有効です。


22

この質問は非常に多くの票を集め、一種のFAQになるので、へstd::vectorの挿入操作の影響に関するC ++ 03とC ++ 11の1つの重要な違いについて言及する別の回答を書く方が良いと思いますイテレータと参照に関しての妥当性reserve()capacity()最もupvoted答えは通知に失敗しました、。

C ++ 03:

再割り当ては、シーケンス内の要素を参照するすべての参照、ポインター、イテレーターを無効にします。挿入によってベクターのサイズが最後のreserve()の呼び出しで指定されたサイズより大きくなるまで、reserve()の呼び出し後の挿入中に再割り当てが行われないことが保証されています。

C ++ 11:

再割り当ては、シーケンス内の要素を参照するすべての参照、ポインター、イテレーターを無効にします。挿入によってベクターのサイズがcapacity()の値より大きくなるまで、reserve()の呼び出し後の挿入中に再割り当てが行われないことが保証されています。

したがって、C ++ 03ではunless the new container size is greater than the previous capacity (in which case all iterators and references are invalidated)、他の回答で述べられているように " greater than the size specified in the most recent call to reserve()" ではなく、 " " である必要があります。これは、C ++ 03がC ++ 11と異なる点の1つです。一度C ++ 03では、insert()以前に指定した値に達するために、ベクトルの大きさの原因となるreserve()コール(よく電流よりも小さくなる可能性がcapacity()あるためreserve()、より大きな可能性がありcapacity()を求めたよりも)、それ以降はinsert()再割り当てと無効化を引き起こす可能性がありますすべてのイテレータと参照。C ++ 11では、これは発生capacity()せず、サイズを超える前に次の再割り当てが行われないことを確実に知ることができますcapacity()

結論として、C ++ 03ベクトルを使用していて、挿入の実行時に再割り当てが発生しないようにしたい場合は、以前に渡した引数の値でありreserve()、サイズをチェックする必要があります。への呼び出しの戻り値capacity()。そうしないと、「時期尚早」の再割り当てに驚かれる可能性があります。


14
しかし、私がこれを行ったコンパイラを撃つと思いますし、その土地の陪審員が私に有罪判決を下すことはありません。
Yakk-Adam Nevraumont、2014年

9
私はこれに「気付かない」ことはありませんでした。これは、C ++ 11で修正されたC ++ 03の編集エラーでした。エラーを利用する主流のコンパイラはありません。
2014

1
@Yakkそのような状況では、gccはすでにイテレータを無効にすると思います。
ShreevatsaR 2016年

2

ここにcppreference.comからの素晴らしい要約表があります

ここに画像の説明を入力してください

ここで、挿入とはコンテナに1つ以上の要素を追加するメソッドを指し、消去とはコンテナから1つ以上の要素を削除するメソッドを指します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.