我不明白,为什么是运行时错误? 迭代时擦除set元素。
set<int> sset;
sset.insert(3);
sset.insert(5);
sset.insert(6);
for(auto s: sset){
sset.erase(s);
}
答案 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
使用了它。
这是未定义的行为。