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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1038708/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 18:30:06  来源:igfitidea点击:

Erase/Remove contents from the map (or any other STL container) while iterating it

c++stl

提问by Aaron Saarela

Allegedly you cannot just erase/remove an element in a container while iterating as iterator becomes invalid. What are the (safe) ways to remove the elements that meet a certain condition? please only stl, no boost or tr1.

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

EDITIs there a more elegant way if I want to erase a number of elements that meet a certain criteria, perhaps with using functor and for_each or erase algorithm ?

编辑如果我想擦除一些满足特定条件的元素,可能使用函子和 for_each 或擦除算法,是否有更优雅的方法?

采纳答案by markh44

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}

回答by Aaron Saarela

You can as long as you don't invalidate your iterator after you've erased it:

只要您在擦除迭代器后不使其无效,您就可以:

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

回答by Viktor Sehr

Example with std::vector

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

}

回答by idbrii

Viktor's solution has the upside of being able to do something with the element before removing. (I wasn't able to do this with remove_ifor remove_copy_if.) But I prefer to use std::find_ifso I never have to increment the iterator myself:

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
}

Where Predicate could be bind1st( equal_to<int>(), 4 )or something like this:

Predicate 可能在哪里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 );
    }
};

回答by Kirill V. Lyadvinsky

I prefer version with while:

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

With whilethere is no danger to increment ittwice as it could be in forloop.

随着while有是增量没有危险it的两倍,因为它可以在for回路中。

回答by Steephen

1.For std::vector<>:

1.对于std::vector<>

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

2.For std::map<>always use std::map::erase()

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. For std::listuse std::list::erase()
  1. std::list使用std::list::erase()

回答by Pascal

markh44 is the most STL-ish response. Note, however, that in general, iterators are invalidated by modifying the container, but set and map are exceptions. There, you can remove items and still go on using the iterators, except if you delete the very item your iterator is referencing.

markh44 是最符合 STL 标准的响应。但是请注意,一般情况下,迭代器会通过修改容器而失效,但 set 和 map 是例外。在那里,您可以删除项目并继续使用迭代器,除非您删除迭代器所引用的项目。

回答by Chad Webb

Use the fact that the post-decrement operator returns a copy of the iterator beforeit decrements. Since the decremented iterator is still valid after erasing the current element, the for loop continues to operate as intended.

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

Edit: Doesn't work if you attempt to erase the first element in the list....

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

回答by TimW

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