web-dev-qa-db-ja.com

反復中にマップ(または他のSTLコンテナー)からコンテンツを消去/削除します

伝えられるところでは、イテレータが無効になるため、反復中にコンテナ内の要素を単に消去/削除することはできません。特定の条件を満たす要素を削除する(安全な)方法は何ですか? stlのみを使用してください。ブーストやtr1は使用しないでください。

[〜#〜] edit [〜#〜]特定の条件を満たす要素の数を消去したい場合、おそらくfunctorとfor_eachまたはeraseアルゴリズムを使用して、よりエレガントな方法はありますか?

26
ultraman
bool IsOdd( int i )
{
    return (i&1)!=0;
}

int a[] = {1,2,3,4,5};
vector<int> v( a, a + 5 );
v.erase( remove_if( v.begin(), v.end(), bind1st( equal_to<int>(), 4 ) ), v.end() );
// v contains {1,2,3,5}
v.erase( remove_if( v.begin(), v.end(), IsOdd ), v.end() );
// v contains {2}
9
markh44

イテレータを消去した後でイテレータを無効にしない限り、次のことができます。

MyContainer::iterator it = myContainer.begin();
while(it != myContainer.end())
{
    if (*it == matchingValue)
    {
       myContainer.erase(it++);
    }
    else
    {
        ++it;
    }
}
33
Aaron Saarela

Std :: vectorの例

#include <vector>

using namespace std;

int main()
{

   typedef vector <int> int_vector;

   int_vector v(10);

   // Fill as: 0,1,2,0,1,2 etc
   for (size_t i = 0; i < v.size(); ++i){
      v[i] = i % 3;
   }

   // Remove every element where value == 1    
   for (int_vector::iterator it = v.begin(); it != v.end(); /* BLANK */){
      if (*it == 1){
         it = v.erase(it);
      } else {
         ++it;
      }
   }

}
10
Viktor Sehr

Viktorのソリューションには、削除する前に要素を使用して何かを実行できるという利点があります。 (私は_remove_if_または_remove_copy_if_でこれを行うことができませんでした。)しかし、私は_std::find_if_を使用することを好むので、イテレーターを自分でインクリメントする必要はありません。

_typedef vector<int> int_vector;
int_vector v;

int_vector::iterator itr = v.begin();
for(;;)
{
    itr = std::find_if(itr, v.end(), Predicate(4));
    if (itr == v.end())
    {
        break;
    }

    // do stuff with *itr here

    itr = v.erase(itr);  // grab a new, valid iterator
}
_

述語はbind1st( equal_to<int>(), 4 )または次のようなものである可能性があります:

_struct Predicate : public unary_function<int, bool>
{
    int mExpected;
    Predicate(int desired) : mExpected(desired) {}
    bool operator() (int input)
    {
        return ( input == mExpected );
    }
};
_
3
idbrii

1. _std::vector<>_の場合:

_std::vector <int> vec;
vec.erase(std::remove(vec.begin(),vec.end(), elem_to_remove), vec.end());
_

2. _std::map<>_の場合は、常にstd::map::erase()を使用します

_std::map<int,std::string> myMap;
myMap.emplace(std::make_pair(1, "Hello"));
myMap.emplace(std::make_pair(2, "Hi"));
myMap.emplace(std::make_pair(3, "How"));
myMap.erase( 1);//Erase with key
myMap.erase(myMap.begin(), ++myMap.begin() );//Erase with range
for( auto &ele: myMap)
{
    if(ele.first ==1)
    {
        myMap.erase(ele.first);//erase by key 
        break; //You can't use ele again properly 
               //wthin this iteration, so break.
    }
}
_
  1. _std::list_の場合は、std::list::erase()を使用します
2
Steephen

whileのバージョンが好きです:

typedef std::list<some_class_t> list_t;
void f( void ) {
  // Remove items from list
  list_t::iterator it = sample_list.begin();
  while ( it != sample_list.end() ) {
    if ( it->condition == true ) {
      it = sample_list.erase( it );
    } else ++it;    
  }
}

whileを使用すると、itループの場合のようにforを2回インクリメントする危険はありません。

markh44は最もSTLっぽい応答です。ただし、一般に、イテレータはコンテナを変更することで無効になりますが、setとmapは例外であることに注意してください。そこで、イテレータが参照しているアイテムそのものを削除する場合を除いて、アイテムを削除し、イテレータを引き続き使用できます。

1
Pascal
template <class Container, class Predicate>
void eraseIf( Container& container, Predicate predicate  ) {
    container.erase( remove_if( container.begin(), container.end(), predicate ), container.end() );
}   

// pre-c++11 version
template<class K, class V, class Predicate> 
void eraseIf( std::map<K,V>& container, Predicate predicate) {
    typename std::map<K,V>::iterator iter = container.begin();
    while(iter!=container.end()) { 
        iterator current = iter++;
        if(predicate(*current))
            container.erase(current);
    }
}

// c++11 version
template<class K, class V, class Predicate> 
void eraseIf( std::map<K,V>& container, Predicate predicate) {
    auto iter = container.begin();
    while(iter!=container.end()) {
        if(predicate(*iter))
            iter = container.erase(iter);
        else
            ++iter;
    }
}
1
TimW

ポストデクリメント演算子がイテレータのコピーを返すという事実を使用してくださいbeforeデクリメントします。デクリメントされたイテレータは現在の要素を消去した後も有効であるため、forループは意図したとおりに動作し続けます。

#include <list>
std::list<int> myList;
for(int i = 0; i < 10; ++i )
{
   myList.Push_back(i);
}

int cnt = 0;
for(std::list<int>::iterator iter = myList.begin(); iter != myList.end(); ++iter)
{
   if( cnt == 5 )
   {
      myList.erase(iter--);
   }
   ++cnt;
}

編集:リストの最初の要素を消去しようとすると機能しません..。

1
Chad Webb