C ++コンテナーのイテレーター無効化ルールとは何ですか?
できれば要約リスト形式で。
(注:これはStack OverflowのC ++ FAQへのエントリになることを目的としています。このフォームでFAQを提供するという考えを批評したい場合は、これをすべて開始したメタへの投稿がそのための場所になります。回答その質問はC ++チャットルームで監視され、FAQのアイデアはそもそも最初から始まっているため、アイデアを思いついた人があなたの答えを読む可能性は非常に高いです。
C ++コンテナーのイテレーター無効化ルールとは何ですか?
できれば要約リスト形式で。
(注:これはStack OverflowのC ++ FAQへのエントリになることを目的としています。このフォームでFAQを提供するという考えを批評したい場合は、これをすべて開始したメタへの投稿がそのための場所になります。回答その質問はC ++チャットルームで監視され、FAQのアイデアはそもそも最初から始まっているため、アイデアを思いついた人があなたの答えを読む可能性は非常に高いです。
回答:
C ++ 17(すべての参照はCPP17- n4659の最終草案からのものです)
シーケンスコンテナー
vector
:関数insert
、emplace_back
、emplace
、push_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]。、、、、、の機能は、この規則の下で覆われています。insert
emplace_front
emplace_back
emplace
push_front
push_back
forward_list
:のオーバーロードinsert_after
はイテレータと参照の有効性に影響を与えません[26.3.9.5/1]
array
:原則として、配列のイテレータは、配列の有効期間を通じて無効になることはありません。ただし、スワップ中、イテレータは同じ配列要素をポイントし続けるため、その値が変更されることに注意してください。
連想コンテナ
All Associative Containers
:insert
およびemplace
メンバーは、イテレータの有効性とコンテナへの参照に影響を与えてはなりません[26.2.6 / 9]順不同の連想コンテナ
All Unordered Associative Containers
:ハッシュを変更すると、イテレータが無効になり、要素間の順序が変更され、要素が表示されるバケットが変更されますが、要素へのポインタや参照は無効になりません。[/ 9 26.2.7] とメンバーは、コンテナ要素への参照の有効性に影響を与えないものとしますが、コンテナにすべてのイテレータを無効にすることができます。[26.2.7 / 14]
ザ及び場合メンバーはイテレータの有効性に影響を与えてはならない場合、挿入操作の前に、容器内の要素の数であり、挿入された要素の数は、あるコンテナのバケット数であり、そしてありますコンテナの最大負荷係数。[26.2.7 / 15]insert
emplace
insert
emplace
(N+n) <= z * B
N
n
B
z
All Unordered Associative Containers
:マージ操作(例a.merge(a2)
:)の場合、転送された要素を参照する反復子と参照するすべての反復子a
は無効になりますが、残っている要素への反復子a2
は有効のままです。(表91 —順不同の連想コンテナの要件)
コンテナアダプター
stack
:基になるコンテナーから継承 queue
:基になるコンテナーから継承 priority_queue
:基になるコンテナーから継承 シーケンスコンテナー
vector
:関数erase
とpop_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]。これが適用されるerase
、pop_front
、pop_back
、clear
機能しています。
remove
とremove_if
メンバー関数:リスト反復子によって参照されるリスト内のすべての要素を消去します。このi
条件については、次の条件が満たされます:*i == value
、pred(*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_list
:erase_after
イテレータと消去された要素への参照のみを無効にします。[26.3.9.5/1]。
remove
とremove_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 Containers
:clear
aの要素を参照するすべての参照、ポインター、イテレーターを無効にし、最後を過ぎたイテレーターを無効にする場合があります(表87-シーケンスコンテナーの要件)。しかし、forward_list
では、clear
過去のイテレータを無効にしません。[26.3.9.5/32]
All Sequence Containers
:assign
コンテナの要素を参照するすべての参照、ポインタ、イテレータを無効にします。以下のためにvector
とdeque
、また過去エンドイテレータを無効にします。(表87-シーケンスコンテナーの要件)
連想コンテナ
All Associative Containers
:erase
メンバーは、イテレーターと消去された要素への参照のみを無効にする必要があります[26.2.6 / 9]
All Associative Containers
:extract
メンバーは、削除された要素への反復子のみを無効にします。削除された要素へのポインタと参照は引き続き有効です[26.2.6 / 10]
コンテナアダプター
stack
:基になるコンテナーから継承 queue
:基になるコンテナーから継承 priority_queue
:基になるコンテナーから継承 イテレーターの無効化に関連する一般的なコンテナー要件:
特に明記されていない限り(明示的に、または他の関数で関数を定義して)、コンテナーメンバー関数を呼び出したり、コンテナーをライブラリ関数の引数として渡したりしても、そのコンテナー内のオブジェクトに対するイテレーターが無効になったり、オブジェクトの値が変更されたりすることはありません。[26.2.1 / 12]
swap()
スワップされるコンテナーの要素を参照する参照、ポインター、イテレーターを無効にする関数はありません。[注:end()イテレータは要素を参照しないため、無効になる可能性があります。-終了ノート] [26.2.1 /(11.6)]
上記の要件の例として:
transform
アルゴリズム:op
and binary_op
関数は、イテレーターまたはサブ範囲を無効にしたり、範囲内の要素を変更したりしてはなりません[28.6.4 / 1]
accumulate
アルゴリズム:[first、last]の範囲でbinary_op
は、要素を変更したり、イテレータやサブ範囲を無効にしたりしない[29.8.2 / 1]
reduce
アルゴリズム:binary_opは、イテレータまたはサブ範囲を無効にしたり、範囲[最初、最後]の要素を変更したりしません。[29.8.3 / 5]
等々...
std::string
ますか?std::vector
SSOによるものとは異なると思います
string
上記の2番目の一般的な要件が満たされません。だから私はそれを含めなかった。また、以前のFAQエントリと同じパターンを維持しようとしました。
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]特に明記されていない限り(明示的に、または他の関数で関数を定義して)、コンテナーメンバー関数を呼び出したり、コンテナーをライブラリー関数の引数として渡したりしても、そのコンテナー内のオブジェクトに対するイテレーターが無効になったり、オブジェクトの値が変更されたりすることはありません。[23.1 / 11]
C ++ 2003では、「終了」イテレータが上記の規則に従うかどうかは明確ではありません。とにかく、それらはそうであると想定する必要があります(これは実際のケースです)。
ポインターの無効化の規則は、参照の無効化の規則と同じです。
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
:(なし)特に明記されていない限り(明示的に、または他の関数で関数を定義して)、コンテナーメンバー関数を呼び出したり、コンテナーをライブラリー関数の引数として渡したりしても、そのコンテナー内のオブジェクトに対するイテレーターが無効になったり、オブジェクトの値が変更されたりすることはありません。[23.2.1 / 11]
swap()関数 は、スワップされるコンテナーの要素を参照する参照、ポインター、またはイテレーターを無効にしません。[注:end()イテレータは要素を参照しないため、無効になる可能性があります。-終了ノート] [23.2.1 / 10]
上記の注意点について以外にswap()
、それは「終わり」のイテレータは、上記コンテナーごとのルールの対象となるかどうかは明らかではありません。とにかく、彼らはそうであると仮定する必要があります。
vector
そして、すべての順序付けreserve(n)
されていない連想コンテナのサポートにより、少なくともコンテナのサイズがになるまで、自動サイズ変更が発生しないことが保証されn
ます。将来の提案では最小負荷係数を指定できるようになるため、順序付けされていない連想コンテナには注意が必要です。これにより、insert
十分なerase
操作によってコンテナサイズが最小値よりも小さくなった後、再ハッシュが発生します。保証は、後に無効になる可能性があると見なされますerase
。
swap()
、コピー/移動割り当て時のイテレータの有効性のルールは何ですか?
std::basic_string
はコンテナとして数えられていないようで、確かに注記が適用される規格のセクションのコンテナではないので、私は間違いを犯したと思います。それでも、SSOが許可されていないのはどこですか(COWは知っています)。
それはどのような種類の挿入反復子は(と付け加えおそらく価値があるstd::back_insert_iterator
、std::front_insert_iterator
、std::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
引き続き有効です。
この質問は非常に多くの票を集め、一種の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()
。そうしないと、「時期尚早」の再割り当てに驚かれる可能性があります。
ここにcppreference.comからの素晴らしい要約表があります:
ここで、挿入とはコンテナに1つ以上の要素を追加するメソッドを指し、消去とはコンテナから1つ以上の要素を削除するメソッドを指します。