迭代时擦除设置元素///

erase set element while iterating///

本文关键字:元素 设置 擦除 迭代      更新时间:2023-10-16

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

set<int> sset;
sset.insert(3);
sset.insert(5);
sset.insert(6);
for(auto s: sset){
sset.erase(s);
}

所以只是为了进一步解释,

你实际上写的是:

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++ 是很棘手的!

遗憾的是,从sset中删除元素将使容器上的隐式迭代无效; 这是由漂亮的语法糖auto s : sset使用的。

这是未定义的行为