web-dev-qa-db-ja.com

イテレーター無効化規則

C++コンテナの反復子無効化規則は何ですか?

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

(注:これは Stack OverflowのC++ FAQ へのエントリになることを意図しています。この形式でFAQを提供するという考えを批判したい場合はそれから これをすべて始めたmetaへの投稿 がそのための場所になるでしょう。その質問への答えは C++ chatroom で監視されます、FAQアイデアは最初から始めたので、あなたの答えはアイデアを思いついた人たちによって読まれる可能性が非常に高いです。)

C++ (出典: イテレータ無効化規則(C++ 03)


挿入

シーケンスコンテナ

  • vector:新しいコンテナサイズが以前の容量を超えない限り、挿入位置より前のすべてのイテレータと参照は影響を受けません(この場合、すべてのイテレータと参照は無効になります)。[23.2.4.3/1]
  • deque:挿入されたメンバが両端キューの最後(前面または背面)にない場合、すべてのイテレータと参照は無効になります(この場合、すべてのイテレータは無効になりますが、要素への参照は影響を受けません)。
  • 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

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

C++ 11(出典: イテレータ無効化規則(C++ 0x)


挿入

シーケンスコンテナ

  • vectorname__:新しいコンテナサイズが以前の容量よりも大きくなければ、挿入位置より前のすべてのイテレータと参照は影響を受けません(この場合、すべてのイテレータと参照は無効になります)[23.3.6.5/1]
  • dequename__:挿入されたメンバが両端キューの末尾(前面または背面)にない限り、すべてのイテレータと参照は無効にされます(この場合、すべてのイテレータは無効にされますが、要素への参照は影響を受けません)。
  • listname__:すべてのイテレータと参照は影響を受けません[23.3.5.4/1]
  • forward_list:すべてのイテレータと参照は影響を受けないinsert_afterに適用)[23.3.4.5/1]
  • arrayname__:(該当なし)

連想コンテナ

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

未分類の連想コンテナ

  • unordered_[multi]{set,map}:すべてのイテレータは再ハッシュが発生すると無効になりましたが、参照は影響を受けません[23.2.5/8]。挿入によってコンテナのサイズがz * Bを超えない場合、再ハッシュ化は行われません。ここで、zname__は最大負荷係数、Bname__は現在のバケット数です。 [23.2.5/14]

コンテナアダプタ

  • stackname__:基礎となるコンテナーから継承された
  • queuename__:基礎となるコンテナーから継承された
  • priority_queue:基礎となるコンテナから継承

消去

シーケンスコンテナ

  • vectorname__:消去点以降のすべてのイテレータと参照は無効になります[23.3.6.5/3]
  • dequename__:最後の要素を消去すると、イテレータと、消去された要素と最後から最後のイテレータへの参照のみが無効になります。最初の要素を消去すると、イテレータと消去された要素への参照だけが無効になります。他の要素を消去すると、すべてのイテレータと参照が無効になります(過去のイテレータを含む)。[23.3.3.4/4]
  • listname__:イテレータと消去された要素への参照だけが無効化されます[23.3.5.4/3]
  • forward_list:イテレータと消去された要素への参照だけが無効化されますerase_afterに適用)[23.3.4.5/1]
  • arrayname__:(該当なし)

連想コンテナ

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

順不同連想コンテナー

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

コンテナアダプタ

  • stackname__:基礎となるコンテナーから継承された
  • queuename__:基礎となるコンテナーから継承された
  • priority_queue:基礎となるコンテナから継承

サイズ変更

  • vectorname__:挿入/消去による[23.3.6.5/12]
  • dequename__:挿入/消去による[23.3.3.3/3]
  • listname__:挿入/消去による[23.3.5.3/1]
  • forward_list:挿入/消去による[23.3.4.5/25]
  • arrayname__:(n/a)

注1

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

注2

no swap()関数は、スワップされるコンテナの要素を参照する参照、ポインタ、または反復子を無効にします。 [注:end()イテレータはどの要素も参照しないので、は無効にされる可能性があります。 - 最後の注記] [23.2.1/10]

注3

swap()に関する上記の警告以外に、 "end"イテレータが上記のコンテナごとの規則に従うかどうかは明らかではありません ;とにかく、あなたは彼らがそうであると仮定するべきです。

注4

vectorname__およびすべての順序なし連想コンテナーは、少なくともコンテナーのサイズがnname__になるまで自動サイズ変更が行われないことを保証するreserve(n)をサポートします。 順不同連想コンテナーでは注意が必要です。十分なinsertname__操作の後にerasename__操作でハッシュの再生成が可能になる最小負荷率の指定が将来の提案で可能になるためです。最小値より下erasename__の後で保証は無効になる可能性があると見なされるべきです。

すべての挿入がこの反復子を介して実行され、他の独立した反復子無効化イベントが発生しない限り、あらゆる種類の挿入反復子(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は引き続き有効なままです。

39
AnT

この質問は非常に多くの票を集めているのでFAQになるので、std::vectorの挿入操作が妥当性に与える影響に関してC++ 03とC++ 11の間の1つの重要な違いについて言及するには別の答えを書くほうがよいでしょう。 reserve()capacity()に関するイテレータと参照の例。

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()の呼び出しの戻り値ではありません。そうしないと、 ""の時期尚早な "の再割り当てに驚かれる可能性があります。

22
neverhoodboy