std::map
内容に基づいてアイテムをループして削除したいと思います。これはどのように行うのが最善でしょうか?
3 に答える
C++11 準拠のコンパイラを使用している場合、これを行う簡単な方法を次に示します。
std::map<K, V>::iterator itr = myMap.begin();
while (itr != myMap.end()) {
if (ShouldDelete(*itr)) {
itr = myMap.erase(itr);
} else {
++itr;
}
}
アイデアは、現在のキーと値のペアを削除する必要があるかどうかを各ステップで確認しながら、コンテナーの先頭から末尾まで反復子を順方向に移動させることです。erase
その場合、メンバー関数を使用して反復された要素を削除し、マップ内の次の要素への反復子を返します。それ以外の場合は、イテレータを通常通り進めます。
C++11 準拠のコンパイラがない場合、または古いコードベースを使用している場合は、少し複雑です。C++11 より前では、erase
メンバー関数はマップ内の次の要素への反復子を返しませんでした。つまり、反復中に要素を削除するには、3 つの部分からなるダンスを使用する必要があります。
- 現在の反復子をコピーします。
- 現在の反復子を次の要素に進めます。
erase
古い反復子のコピーを呼び出します。
これを次に示します。
std::map<K, V>::iterator itr = myMap.begin();
while (itr != myMap.end()) {
if (ShouldDelete(*itr)) {
std::map<K, V>::iterator toErase = itr;
++itr;
myMap.erase(toErase);
} else {
++itr;
}
}
このプロセスが必要だっerase
たのは、イテレータを呼び出しただけでは無効になり、インクリメントやデクリメントなどの操作によって未定義の動作が発生する可能性があるためです。上記のコードは、イテレータのコピーを設定しitr
、次の要素になるように進めてから、イテレータの一時コピーを消去することで、これを回避しています。
巧妙なトリッキーを使用すると、読みやすさを犠牲にしてこのコードを縮小することができます。次のパターンは古い C++ コードでは一般的ですが、C++11 では必要ありません。
std::map<K, V>::iterator itr = myMap.begin();
while (itr != myMap.end()) {
if (ShouldDelete(*itr)) {
myMap.erase(itr++); // <--- Note the post-increment!
} else {
++itr;
}
}
ここでの後置インクリメント演算子の使用は、古いイテレータのコピーを作成する (後置 ++ 演算子は元のイテレータ値のコピーを返すことを思い出してください) と同時に、古いイテレータを進める賢い方法です。
for(MyMap::iterator it = mymap.begin(); it!=mymap.end(); ) {
if(mycondition(it))
it = mymap.erase(it);
else
it++;
}
編集:これはMSVCでのみ機能するようです
edit2: c++0x では、これは連想コンテナでも機能します
これは簡単な方法の 1 つです。
int value_to_delete( 2 );
for( std::map<int, int>::iterator i = mm.begin(); i != mm.end(); ) {
if( i->second != value_to_delete ) {
mm.erase( i++ ); // advance before iterator become invalid
}
else {
++i;
}
}