【发布时间】:2010-11-05 12:56:59
【问题描述】:
据称,当迭代器变得无效时,您不能在迭代时擦除/删除容器中的元素。删除满足特定条件的元素的(安全)方法是什么?请只使用 stl,不要使用 boost 或 tr1。
编辑 如果我想删除一些符合特定标准的元素,是否有更优雅的方法,也许使用 functor 和 for_each 或擦除算法?
【问题讨论】:
据称,当迭代器变得无效时,您不能在迭代时擦除/删除容器中的元素。删除满足特定条件的元素的(安全)方法是什么?请只使用 stl,不要使用 boost 或 tr1。
编辑 如果我想删除一些符合特定标准的元素,是否有更优雅的方法,也许使用 functor 和 for_each 或擦除算法?
【问题讨论】:
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;
}
}
【讨论】:
if(cond) it = c.erase(it); else ++it; .. 并且不要按照惯例在 for 中增加 it!
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.
}
}
std::list 使用std::list::erase()
【讨论】:
使用后减运算符返回迭代器的副本在它减量的事实。由于在擦除当前元素后递减的迭代器仍然有效,因此 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;
}
编辑:如果您尝试擦除列表中的第一个元素,则不起作用....
【讨论】:
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 );
}
};
【讨论】:
markh44 是最接近 STL 的响应。 但是请注意,一般情况下,通过修改容器会使迭代器失效,但 set 和 map 是例外。在那里,您可以删除项目并继续使用迭代器,除非您删除了迭代器正在引用的项目。
【讨论】:
我更喜欢带有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 循环中是没有危险的。
【讨论】:
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}
【讨论】:
只要在删除迭代器后不使其失效,就可以:
MyContainer::iterator it = myContainer.begin();
while(it != myContainer.end())
{
if (*it == matchingValue)
{
myContainer.erase(it++);
}
else
{
++it;
}
}
【讨论】:
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;
}
}
}
【讨论】: