在迭代时从地图(或任何其他STL容器)中删除/删除内容

时间:2009-06-24 14:26:02

标签: c++ stl

据称,当迭代器变为无效时,您不能在迭代时擦除/删除容器中的元素。删除满足特定条件的元素的(安全)方法是什么?请只是stl,no boost或tr1。

修改 如果我想删除符合特定条件的多个元素,或许使用functor和for_each或erase算法,是否有更优雅的方法?

9 个答案:

答案 0 :(得分:34)

只要在删除迭代器后它就不会使迭代器无效:

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

答案 1 :(得分:9)

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;
      }
   }

}

答案 2 :(得分:9)

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}

答案 3 :(得分:3)

Viktor的解决方案具有在移除之前能够对元素执行某些操作的好处。 (我无法使用remove_ifremove_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 );
    }
};

答案 4 :(得分:2)

我更喜欢带有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;    
  }
}

使用whileit循环增加for两次没有危险。

答案 5 :(得分:2)

1.For 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()

答案 6 :(得分:1)

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;
    }
}

答案 7 :(得分:1)

markh44是最STL-ish的反应。 但请注意,通常,通过修改容器使迭代器无效,但set和map是异常。在那里,您可以删除项目并继续使用迭代器,除非您删除了迭代器引用的项目。

答案 8 :(得分:1)

使用post-decrement运算符在递减之前返回迭代器的副本这一事实。由于在删除当前元素后递减的迭代器仍然有效,因此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;
}

编辑:如果您尝试删除列表中的第一个元素,则不起作用....