迭代时擦除set元素

时间:2018-07-06 10:36:27

标签: c++

我不明白,为什么是运行时错误? 迭代时擦除set元素。

set<int> sset;
sset.insert(3);
sset.insert(5);
sset.insert(6);

for(auto s: sset){
    sset.erase(s);
}

2 个答案:

答案 0 :(得分:4)

因此,为了进一步解释,

您实际写的是:

for (set<int>::const_iterator i=sset.begin(), e=sset.end(); i != e; i++)
{
    auto s = *i;
    sset.erase(s);
}

因此,问题在于擦除时,内部迭代器i无效。尝试顺序删除许多容器中的内容是一种普遍的痛苦。

出于相同的原因,以下更传统的顺序删除代码也很糟糕,但也许更明显:

for (set<int>::iterator i=sset.begin(), e=sset.end(); i != e; i++)
{
    sset.erase(i);
}

修复:

通常,在可能的情况下,更容易依赖整个容器的上下文交换销毁:

C++98: SsetType().swap(sset); 
C++11: sset = decltype<sset>();

您可以这样做:

sset.erase(sset.begin(), sset.end());

解决此问题的另一种方法是继续删除begin(),直到集合为empty()

但是所有这些的问题是,您不能轻易地将它们扩展为有条件地擦除要迭代的集合中的成员。是的,也有用于条件擦除的助手,它们可以与lambda一起使用,因此它们可以承载状态,但是它们通常比滚动自己的循环难用。

自c ++ 11起,set :: erase(iterator)返回一个可以继续进行迭代的新迭代器,因此您可以编写:

for (set<int>::iterator i=sset.begin(), e=sset.end(); i != e; )
{
    i = sset.erase(i);
}

如果您要进行一些条件测试,则:

for (set<int>::iterator i=sset.begin(), e=sset.end(); i != e; )
{
    if ( ... condition ... )
        i = sset.erase(i);
    else
        i++;
}

之前,在c ++ 98中,您应该编写如下内容:

for (set<int>::iterator i=sset.begin(), e=sset.end(); i != e; )
{
    auto j = i;
    j++;
    if ( ... condition ... )
        i = sset.erase(i);
    i = j;
}

作为练习,您可以将j的使用扩展到for语句中。但是,在C98中获得初始j ++会很棘手!

答案 1 :(得分:3)

遗憾的是,擦除sset中的元素将使容器上的隐式迭代无效 ;精美的语法糖auto s : sset使用了它。

这是未定义的行为