在迭代std :: list时擦除

时间:2013-04-29 00:43:32

标签: c++ list

如果我在iterator循环中使用for并且在迭代器的当前迭代中使用erase,那么for循环应该继续正常并访问其余的{ {1}}元素?

从我所读到的情况来看,这应该是这种情况,是listlistdeque的主要区别特征。就我的目的而言,vector可能有用,但我需要这种行为。

这是我正在考虑的循环:

queue

3 个答案:

答案 0 :(得分:48)

编写该循环的惯用方法是:

for (auto i = list.begin(); i != list.end();) {
    if (condition)
        i = list.erase(i);
    else
        ++i;
}

您可以使用setmultisetmapmultimap执行相同的操作。对于这些容器,您可以擦除元素,而不会影响对其他元素的任何迭代器的有效性。其他容器如vectordeque并不那么友善。对于那些容器,只有擦除迭代器之前的元素保持不变。这种差异仅仅是因为list在单独分配的节点中存储元素。一个链接很容易。 vector是连续的,取出一个元素后,将所有元素移回一个位置后。

你的循环被破坏是因为你在某些特定条件下删除i处的元素。在该调用之后,i不再是有效的迭代器。然后,您的for循环会增加i,但i无效。地狱般的地狱随之而来。这就是为什么erase在删除之后将迭代器返回到元素的确切情况......所以你可以继续遍历list

您也可以使用list::remove_if

list.remove_if([](auto& i) { return i > 10; });

在lambda中,如果要删除元素,则返回true。在此示例中,它将删除大于10的所有元素。

答案 1 :(得分:0)

for (auto i = list.begin(); i != list.end(); ++i) {
    if (condition) {
        list.erase(i);
        --i;
    }
}

答案 2 :(得分:0)

如果您只想用于迭代器,则可以通过这种方式使用它,例如:

list<int> lst{4, 1, 2, 3, 5};

for(auto it = lst.begin(); it != lst.end();++it){
    if ((*it % 2) == 1){
        it = lst.erase(it);  erase and go to next(erase will return the next iterator)
        --it;  // as it will be add again in for, so we go back one step
    }
}

for(auto it:lst)cout<<it<<" ";
cout<<endl;  //4 2 

但是在while迭代器中擦除将更加清晰:

list<int> lst{4, 1, 2, 3, 5};

auto it = lst.begin();
while (it != lst.end()){
    if((*it % 2) == 1){
        it = lst.erase(it);// erase and go to next
    } else{
        ++it;  // go to next
    }
}

for(auto it:lst)cout<<it<<" ";
cout<<endl;  //4 2

您还可以使用成员函数remove_if

list<int> lst{4, 1, 2, 3, 5};

lst.remove_if([](int a){return a % 2 == 1;});

for(auto it:lst)cout<<it<<" ";
cout<<endl;  //4 2

或将std::remove_if结合使用erase功能:

list<int> lst{4, 1, 2, 3, 5};

lst.erase(std::remove_if(lst.begin(), lst.end(), [](int a){
    return a % 2 == 1;
}), lst.end());

for(auto it:lst)cout<<it<<" ";
cout<<endl;  //4 2

您还可以参考以下问题: Removing item from vector, while in C++11 range 'for' loop?