迭代 std::list 时擦除
Posted
技术标签:
【中文标题】迭代 std::list 时擦除【英文标题】:Erasing while iterating an std::list 【发布时间】:2013-04-29 00:43:32 【问题描述】:如果我在 for
循环中使用 iterator
并且在迭代器的当前迭代中使用 erase
,那么 for 循环应该继续正常并访问其余的 list
元素?
根据我的阅读,应该是这种情况,并且是list
与deque
或vector
的主要区别特征。出于我的目的,queue
可能有效,但我需要这种行为。
这是我正在考虑的循环:
std::list<Sequence>::iterator iterator;
iterator=m_concurrents.begin();
for (;iterator!=m_concurrents.end();++iterator)
if (iterator->passes())
m_concurrents.erase(iterator);
【问题讨论】:
【参考方案1】:编写该循环的惯用方式是:
for (auto i = list.begin(); i != list.end();)
if (condition)
i = list.erase(i);
else
++i;
您可以使用set
、multiset
、map
或multimap
执行相同的操作。对于这些容器,您可以擦除元素而不影响任何迭代器对其他元素的有效性。像vector
或deque
这样的其他容器就不那么友好了。对于那些容器,只有擦除迭代器之前的元素保持不变。这种差异仅仅是因为list
s 将元素存储在单独分配的节点中。取出一个链接很容易。 vector
s 是连续的,取出一个元素会将其后的所有元素移回一个位置。
您的循环已损坏,因为您在某些给定条件下擦除了i
处的元素。在该调用之后,i
不再是有效的迭代器。然后您的for
循环递增i
,但i
无效。人间地狱随之而来。这就是为什么erase
在删除后将迭代器返回到元素的确切情况......所以你可以继续遍历list
。
你也可以使用list::remove_if
:
list.remove_if([](auto& i) return i > 10; );
在 lambda 中,如果要删除元素,则返回 true。在本例中,它将删除所有大于 10 的元素。
【讨论】:
在这个例子中,一旦迭代被擦除,您将停止迭代,因此不会迭代列表以可能查看其他元素。我对迭代整个列表并根据需要删除条目很感兴趣。据我了解,这对于list
是安全的,但对于其他容器则不然。这是我问题的核心,如果不清楚,请见谅。
这不适用于非 C++11 编译器。擦除成员方法仅在 C++03 中返回 void
。将 i = list.erase(i)
更改为 list.erase(i++)
将修复它。
我很困惑,抱歉。仅当迭代器为 !cheesecake
时才递增迭代器。那么当它是 cheesecake
时它是如何递增的呢?
啊,我明白了。 i
在erase
返回时自动递增。我看到您正在使用 auto
将 i
隐式指定为 iterator
。仍然没有解释为什么我的 Q 中的循环“坏了”。看起来我在做类似的事情,只是风格不同。
也可以考虑list.erase(std::remove_if(list.begin(), list.end(), condition), list.end())
【参考方案2】:
for (auto i = list.begin(); i != list.end(); ++i)
if (condition)
list.erase(i);
--i;
【讨论】:
不要那样做。如果要删除第一个元素,它会“繁荣”。【参考方案3】:如果只想使用for迭代器,可以这样使用,例如:
list<int> lst4, 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> lst4, 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> lst4, 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> lst4, 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?
【讨论】:
以上是关于迭代 std::list 时擦除的主要内容,如果未能解决你的问题,请参考以下文章
如何在迭代boost :: intrusive :: list时擦除