web-dev-qa-db-ja.com

STLコンテナから要素を消去するにはどうすればよいですか?

指定されたを持っている、またはいくつかの条件を満たすSTLコンテナから要素を消去するにはどうすればよいですか?

さまざまな種類のコンテナに対してそれを行うための単一の一般的または統一された方法はありますか?

39
Mr.C64

残念ながら、STLコンテナから要素を消去するための単一の均一インターフェイスまたはパターンはありません。しかし、次の3つの動作が発生します。

std :: vectorパターン

_std::vector_から特定の条件を満たす要素を消去するための一般的な手法は、いわゆる 消去-削除イディオム

vが_std::vector_のインスタンスであり、値xの要素をベクトルから消去する場合は、次のようなコードを使用できます。

_// Erase elements having value "x" from vector "v"
v.erase( std::remove(v.begin(), v.end(), x), v.end() );
_

要素を消去するために満たす基準が単純な "消去する要素== x"よりも複雑な場合は、代わりにstd::remove_if()アルゴリズムを使用できます。 std::remove()の:

_// Erase elements matching "erasing_condition" from vector "v"
v.erase( std::remove_if(v.begin(), v.end(), erasing_condition), v.end() );
_

ここで、_erasing_condition_は単項述語であり、いくつかの形式で表すことができます。bool-戻り関数ベクトル要素タイプを入力として受け取ることができます(したがって、戻り値がtrueの場合、要素はベクトルから消去されます;それがfalseの場合、消去されません);または、in-linelambdaとして表すことができます。 functor ;等.

std::remove()std::remove_if()はどちらも_<algorithm>_ヘッダーからの一般的なアルゴリズムです。)

ここに明確な説明があります ウィキペディアから

algorithmライブラリは、このためのremoveおよび_remove_if_アルゴリズムを提供します。これらのアルゴリズムは、2つの順方向イテレーターによって示される要素の範囲で動作するため、基になるコンテナーまたはコレクションについての知識がありません。したがって、実際にコンテナから削除される要素はありません。むしろ、削除基準に適合しないすべての要素は、同じ相対的な順序で範囲の先頭にまとめられます。残りの要素は有効ですが、指定されていない状態のままです。これが行われると、removeは、最後の削除されていない要素の1つ先を指すイテレータを返します。

コンテナから要素を実際に削除するために、removeはコンテナのeraseメンバー関数と組み合わされているため、「erase-removeidiom」という名前が付けられています。

基本的に、std::remove()およびstd::remove_if()は、範囲の前(つまり最初)の消去基準を満たさない要素を圧縮しますvector)の次に、erase()は実際に残りの要素をコンテナから削除します。

このパターンは、_std::deque_などの他のコンテナーにも適用されます。

std :: listパターン

_std::list_から要素を消去するには、単純なremove()およびremove_if()メソッドが利用可能です:

_// Erase elements having value "x" from list "l"
l.remove( x )

// Erase elements satisfying "erasing_condition" from list "l"
l.remove_if( erasing_condition );
_

(ここで、_erasing_condition_は単項述語であり、上記のセクションのstd::remove_if()について説明したのと同じ特性を備えています。)

_std::forward_list_のように、同じパターンを同様のコンテナーに適用できます。

連想コンテナ(例:std :: map、std :: set、...)パターン

_std::map__std::set_ /のような連想コンテナ_std::unordered_map_などは、ここで説明する一般的なパターンに従います。

  1. 消去条件が単純なキーマッチングである場合(つまり、 "キーxを持つ要素を消去する")、単純なerase() methodは次のように呼び出すことができます:

    _// Erase element having key "k" from map "m":
    m.erase( k );
    _
  2. 消去条件がより複雑で、カスタムの単項述語で表される場合(たとえば、 "すべての奇数要素を消去")、forループを使用できます。 (ループ本体で明示的な消去条件チェックを行い、erase(iterator)メソッドを呼び出します):

    _//
    // Erase all elements from associative container "c", satisfying "erasing_condition":
    //
    for (auto it = c.begin(); it != c.end(); /* "it" updated inside loop body */ )
    {
        if ( erasing_condition(*it) )
        {   
            // Erase the element matching the specified condition 
            // from the associative container.
            it = c.erase(it);
    
            // Note:
            // erase() returns an iterator to the element 
            // that follows the last element removed, 
            // so we can continue the "for" loop iteration from that position.
        }
        else
        {
            // Current element does _not_ satisfy erasing condition,
            // so we can just move on to the next element.
            ++it;
        }       
    }     
    _

統一されたアプローチの必要性

上記の分析からわかるように、残念ながら、STLコンテナから要素を消去するための統一された一般的なアプローチはありません。

次の表は、前述のパターンをまとめたものです。

_----------------+------------------------------------------             
   Container    |            Erasing Pattern
----------------+------------------------------------------                
                |
 vector         |    Use erase-remove idiom.
 deque          |
                |
----------------+------------------------------------------               
                |
 list           |    Call remove()/remove_if() methods.
 forward_list   |
                |
----------------+------------------------------------------  
                |
 map            |    Simple erase(key) method call, 
 set            |    or 
 unordered_map  |    loop through the container,
 multimap       |    and call erase(iterator) on matching
                |    condition.
 ...            |
                |
----------------+------------------------------------------
_

特定のコンテナに基づいて異なる特定のコードを書くと、エラーが発生しやすく、保守が難しく、読みにくいなどの問題が発生します。

ただし、異なるコンテナタイプに対して共通の名前(例:erase()およびerase_if()overloadedを使用して関数テンプレートを記述し、これらの関数での前述のパターンの実装。
したがって、クライアントはこれらのerase()およびerase_if()ジェネリック関数を呼び出すだけで、コンパイラーは呼び出しを適切な実装にディスパッチします()コンテナタイプに基づくコンパイル時間)。

テンプレートメタプログラミング手法を使用した、より洗練されたアプローチが提示されています Stephan T. Lavavejhere

55
Mr.C64