我在迭代时可以从std :: list中删除元素吗?

时间:2010-11-23 21:03:07

标签: c++ stl

当我在迭代时,我可以从std :: list中删除元素吗?例如:

std::list<int> lst;
//....
for (std::list<int> itr = lst.begin(); itr != lst.end(); itr++)
{
    if (*itr > 10)
         lst.remove(*itr);
}

? 为什么?

5 个答案:

答案 0 :(得分:33)

正确的代码如下:

for (std::list<int>::iterator itr = lst.begin(); itr != lst.end(); /*nothing*/)
{
    if (*itr > 10)
        itr = lst.erase(itr);
    else
        ++itr;
}

当你从列表中删除一个项目时,你可能会使迭代器失效(如果它指向要删除的项目。)因此你需要使用erase删除它(它返回一个指向下一个项目的有效迭代器) )。

更好的想法是使用std::remove_if

bool greater_than_10(int x)
{
    return x > 10;
}

lst.remove_if(greater_than_10);

如果你的编译器支持lambdas,你可以把它缩短:

lst.remove_if([](int x){ return x > 10; });

(我没有测试这段代码,因为我的编译器不是那么新;感谢lambda函数从@John Dibling的答案中偷走了。)


实际上,从列表中删除会使only the iterators pointing to the item being deleted无效。但请注意,其他STL容器没有此属性。


因此,简而言之:一般来说,在迭代它时不应该从列表中删除项目,因为删除可能使迭代器无效(并且程序可能会崩溃)。但是,如果您完全确定删除的项目不是删除时使用的任何迭代器引用的值,则可以删除。

请注意,对于其他STL容器(例如向量),约束甚至更严格:从容器中删除不仅会使指向已删除项目的迭代器失效,还可能使其他迭代器失效!因此,在迭代它们时从这些容器中删除更有问题。

答案 1 :(得分:9)

没有。示例代码使itr无效,从而导致未定义的行为。但这可行:

for (std::list<int>::iterator itr = lst.begin(); itr != lst.end(); )
{
    if (*itr > 10)
        itr = lst.erase(itr);
    else
        ++itr;
}

答案 2 :(得分:3)

不,你不能。

但你可以(而且应该)使用std::remove_if以及一个写着“大于10”的仿函数,如下所示:

#include <list>
#include <algorithm>


int main()
{
    std::list<int> lst;
    lst.push_back(1);
    lst.push_back(12);
    lst.push_back(1);
    //....
    lst.erase(std::remove_if(lst.begin(), lst.end(), std::bind2nd(std::greater<int>(), 10)), lst.end());
}

另一种更通用的方法是编写自己的自定义函子。这是一个仿函数is_a_match,如果要检查的值大于10,则返回true。您可以重新定义operator()以返回true以对应于您的情况中的任何含义“匹配”:

#include <list>
#include <algorithm>
#include <functional>

struct is_a_match : public std::unary_function<int, bool>
{
    is_a_match(int val) : val_(val) {};
    bool operator()(int victim) const { return victim > val_; }
private:
    int val_;
};

int main()
{
    std::list<int> lst;
    lst.push_back(1);
    lst.push_back(12);
    lst.push_back(1);
    //....
    lst.erase(std::remove_if(lst.begin(), lst.end(), is_a_match(10) ));
}

如果你有C ++ 0x符合编译器的好处,你也可以使用lambdas,它可以摆脱仿函数并在许多情况下编写更具表现力的代码

#include <list>
#include <algorithm>

int main()
{
    std::list<int> lst;
    lst.push_back(1);
    lst.push_back(12);
    lst.push_back(1);
    //....
    lst.erase(std::remove_if(lst.begin(), lst.end(), [](int v) {return v > 10;}));
}

答案 3 :(得分:0)

我认为你可以,但是你必须在删除元素后重新分配迭代器,这可以使用erase方法而不是remove来完成。

否则它将是不安全的,不应该这样做。

答案 4 :(得分:0)

有关迭代器的描述,请参阅http://www.cppreference.com/wiki/iterator/start

几点说明:

  • 您应该使用预增量运算符(++itr)而不是增量后运算符(itr++
  • 失效取决于迭代器及其关联集合的确切实现。