std::map
をループし、その内容に基づいてアイテムを削除したいと思います。これはどの程度最善ですか?
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
を呼び出した場合、invalidate itになるため、このプロセスが必要でした。これは、インクリメントやデクリメントなどの操作が未定義の動作につながることを意味します。上記のコードは、イテレータのコピーをセットアップし、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;
}
}
ここでポストインクリメント演算子を使用することは、古いイテレータのコピーを作成する賢い方法です(postfix ++演算子は元のイテレータ値のコピーを返すことに注意してください)一方で、古いイテレータも進めます。
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;
}
}