3

次のコードの背後にある理論的根拠に興味があります。特定のマップについて、end()次のコードを使用して、(明らかに)までの範囲を削除できますが、これは含まれません。

map<string, int> myMap;
myMap["one"] = 1;
myMap["two"] = 2;
myMap["three"] = 3;

map<string, int>::iterator it = myMap.find("two");

myMap.erase( it, myMap.end() );

これにより、範囲を使用して最後の2つのアイテムが消去されます。ただし、単一のイテレータバージョンの消去を使用した場合myMap.end()、イテレータは明らかにコレクションの最後にあるため、パスしてもアクションが発生しないと半分は予想していました。これは、明らかに未定義の動作につながる破損または無効なイテレータとは異なります。

しかし、私がこれを行うとき:

myMap.erase( myMap.end() );

セグメンテーション違反が発生します。end()マップがイテレータが等しいかどうかを確認し、その場合はアクションを実行しないことは難しいとは思いませんでした。私が行方不明になっているこれには微妙な理由がありますか?これでも機能することに気づきました。

myMap.erase( myMap.end(), myMap.end() );

(つまり、何もしません)

私が尋ねる理由は、コレクションへの有効なイテレータを受け取るコードがあり(ただし、そうなる可能性がありますend())、最初に次のようにチェックするのではなく、単にこれを消去に渡すことを望んでいたためです。

if ( it != myMap.end() )
    myMap.erase( it );

これは私には少し不格好に思えます。別の方法は、キータイプによる消去オーバーロードを使用できるようにコードを書き直すことですが、それを助けることができれば、書き直しすぎないようにします。

4

3 に答える 3

5

重要なのは、標準ライブラリでは、2 つの反復子によって決定される範囲が半開きの範囲であるということです。数学表記[a,b)では最初の反復子は含まれますが、最後の反復子は含まれません (両方が同じ場合、範囲は空です)。同時に、半開放範囲表記に完全に一致する、最後の要素の 1 つ後ろend()にある反復子を返します。

範囲バージョンを使用するeraseと、最後の反復子によって参照される要素を削除しようとすることはありません。変更された例を考えてみましょう:

map<int,int> m;
for (int i = 0; i < 5; ++i)
   m[i] = i;
m.erase( m.find(1), m.find(4) );

実行の最後に、マップは と の 2 つのキーを保持し0ます4。2 番目の反復子によって参照される要素は、コンテナーから消去されていないことに注意してください。

一方、単一の反復子操作は、反復子によって参照される要素を消去します。上記のコードが次のように変更された場合:

for (int i = 1; i <= 4; ++i ) 
   m.erase( m.find(i) );

キー4を持つ要素が削除されます。あなたの場合、有効なオブジェクトを参照していない終了イテレータを削除しようとします。

イテレータが end() に等しいかどうかをマップがチェックし、その場合にアクションを実行しないことは難しいとは思いませんでした。

いいえ、難しいことではありませんが、関数は別の契約を念頭に置いて設計されています。呼び出し元はコンテナー内の要素にイテレーターを渡す必要があります。この理由の一部は、C++ ではほとんどの機能が可能な限り最小限のコストで済むように設計されているため、ユーザーは安全性とパフォーマンスのバランスを取ることができます。ユーザーは を呼び出す前にイテレータをテストできますeraseが、そのテストがライブラリ内にある場合、ユーザーはイテレータが有効であることを知っていてもテストをオプトアウトできません。

于 2012-09-14T12:39:56.770 に答える
1

n3337 23.2.4 表 102

a.erase( q1, q2)

範囲 [q1,q2) 内 のすべての要素を消去します。q2 を返します。

そのため、の場合、iteratorからの戻りmap::end()は範囲外ですmyMap.erase(myMap.end(), myMap.end())

a.erase(q)

q が指す要素を消去します。要素が削除される直前の q の直後の要素を指す反復子を返します。そのような要素が存在しない場合は、a.end() を返します。

イテレータが end() に等しいかどうかをマップがチェックし、その場合にアクションを実行しないことは難しいとは思いませんでした。私が見逃している微妙な理由はありますか?

理由は同じstd::vector::operator[]です。もちろん、インデックスが範囲内にあることを確認できません。

于 2012-09-14T12:18:05.790 に答える
1

2 つの反復子を使用して範囲を指定する場合、範囲は、最初の反復子が指す要素から 2 番目の反復子が指す要素までの要素で構成されます。したがってerase(it, myMap.end())、 から までのすべてを消去するように指示されますitが、 は含まれませんend()。「実際の」要素を指す反復子を 2 番目の要素として同様に渡すことができ、その反復子が指す要素は消去されません。

使用すると、指してerase(it)いる要素を消去するように指示されます。イテレータは有効な要素を指していないため、意味のあるitことは何もしません。ライブラリがこの状況を診断することは可能であり、デバッグ ライブラリはそれを行いますが、イテレータが何を指しているかをチェックするために を呼び出すたびにコストがかかります。標準ライブラリは、ユーザーにそのコストを課しません。あなたは自分でいます。end()erase(end())erase<g>

于 2012-09-14T12:27:45.280 に答える