C++ 您可以在迭代时从 std::list 中删除元素吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/596162/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Can you remove elements from a std::list while iterating through it?
提问by AShelly
I've got code that looks like this:
我有看起来像这样的代码:
for (std::list<item*>::iterator i=items.begin();i!=items.end();i++)
{
bool isActive = (*i)->update();
//if (!isActive)
// items.remove(*i);
//else
other_code_involving(*i);
}
items.remove_if(CheckItemNotActive);
I'd like remove inactive items immediately after update them, inorder to avoid walking the list again. But if I add the commented-out lines, I get an error when I get to i++
: "List iterator not incrementable". I tried some alternates which didn't increment in the for statement, but I couldn't get anything to work.
我想在更新它们后立即删除不活动的项目,以避免再次遍历列表。但是如果我添加注释掉的行,当我到达时我会收到一个错误i++
:“列表迭代器不可增加”。我尝试了一些在 for 语句中没有增加的替代方案,但我无法得到任何工作。
What's the best way to remove items as you are walking a std::list?
在走 std::list 时删除项目的最佳方法是什么?
回答by Michael Kristofik
You have to increment the iterator first (with i++) and then remove the previous element (e.g., by using the returned value from i++). You can change the code to a while loop like so:
您必须首先增加迭代器(使用 i++),然后删除前一个元素(例如,通过使用 i++ 的返回值)。您可以将代码更改为 while 循环,如下所示:
std::list<item*>::iterator i = items.begin();
while (i != items.end())
{
bool isActive = (*i)->update();
if (!isActive)
{
items.erase(i++); // alternatively, i = items.erase(i);
}
else
{
other_code_involving(*i);
++i;
}
}
回答by MSN
You want to do:
你想做:
i= items.erase(i);
That will correctly update the iterator to point to the location after the iterator you removed.
这将正确更新迭代器以指向您删除迭代器后的位置。
回答by Mike
You need to do the combination of Kristo's answer and MSN's:
您需要结合 Kristo 的回答和 MSN 的回答:
// Note: Using the pre-increment operator is preferred for iterators because
// there can be a performance gain.
//
// Note: As long as you are iterating from beginning to end, without inserting
// along the way you can safely save end once; otherwise get it at the
// top of each loop.
std::list< item * >::iterator iter = items.begin();
std::list< item * >::iterator end = items.end();
while (iter != end)
{
item * pItem = *iter;
if (pItem->update() == true)
{
other_code_involving(pItem);
++iter;
}
else
{
// BTW, who is deleting pItem, a.k.a. (*iter)?
iter = items.erase(iter);
}
}
Of course, the most efficient and SuperCool® STL savy thing would be something like this:
当然,最高效、最精明的 SuperCool® STL 应该是这样的:
// This implementation of update executes other_code_involving(Item *) if
// this instance needs updating.
//
// This method returns true if this still needs future updates.
//
bool Item::update(void)
{
if (m_needsUpdates == true)
{
m_needsUpdates = other_code_involving(this);
}
return (m_needsUpdates);
}
// This call does everything the previous loop did!!! (Including the fact
// that it isn't deleting the items that are erased!)
items.remove_if(std::not1(std::mem_fun(&Item::update)));
回答by Mykola Golubyev
Use std::remove_if algorithm.
使用 std::remove_if 算法。
Edit:Work with collections should be like: 1. prepare collection. 2. process collection.
编辑:处理集合应该是这样的: 1. 准备集合。2.流程收集。
Life will be easier if you won't mix this steps.
如果您不混合这些步骤,生活会更轻松。
- std::remove_if. or list::remove_if ( if you know that you work with list and not with the TCollection )
- std::for_each
- std::remove_if。或 list::remove_if (如果您知道您使用的是 list 而不是 TCollection )
- std::for_each
回答by David Cormack
Here's an example using a for
loop that iterates the list and increments or revalidates the iterator in the event of an item being removed during traversal of the list.
这是一个使用for
循环的示例,该循环迭代列表并在遍历列表期间删除项目时递增或重新验证迭代器。
for(auto i = items.begin(); i != items.end();)
{
if(bool isActive = (*i)->update())
{
other_code_involving(*i);
++i;
}
else
{
i = items.erase(i);
}
}
items.remove_if(CheckItemNotActive);
回答by Rafael Gago
The alternative for loop version to Kristo's answer.
Kristo 答案的替代 for 循环版本。
You lose some efficiency, you go backwards and then forward again when deleting but in exchange for the extra iterator increment you can have the iterator declared in the loop scope and the code looking a bit cleaner. What to choose depends on priorities of the moment.
你失去了一些效率,你在删除时向后然后再次向前,但作为额外的迭代器增量的交换,你可以在循环范围内声明迭代器,并且代码看起来更清晰。选择什么取决于当下的优先事项。
The answer was totally out of time, I know...
答案完全不合时宜,我知道......
typedef std::list<item*>::iterator item_iterator;
for(item_iterator i = items.begin(); i != items.end(); ++i)
{
bool isActive = (*i)->update();
if (!isActive)
{
items.erase(i--);
}
else
{
other_code_involving(*i);
}
}
回答by Jayhello
I have sumup it, here is the three method with example:
我总结了一下,这里是三个方法的例子:
1. using while
loop
1.使用while
循环
list<int> lst{4, 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
2. using remove_if
member funtion in list:
2.remove_if
在列表中使用成员函数:
list<int> lst{4, 1, 2, 3, 5};
lst.remove_if([](int a){return a % 2 == 1;});
for(auto it:lst)cout<<it<<" ";
cout<<endl; //4 2
3. using std::remove_if
funtion combining with erase
member function:
3.使用std::remove_if
函数结合erase
成员函数:
list<int> lst{4, 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
4. using for
loop , should note update the iterator:
4. 使用for
loop ,要注意更新迭代器:
list<int> lst{4, 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
回答by sancho.s ReinstateMonicaCellio
Iterating backwards avoids the effect of erasing an element on the remaining elements to be traversed:
向后迭代避免了擦除元素对要遍历的剩余元素的影响:
typedef list<item*> list_t;
for ( list_t::iterator it = items.end() ; it != items.begin() ; ) {
--it;
bool remove = <determine whether to remove>
if ( remove ) {
items.erase( it );
}
}
PS: see this, e.g., regarding backward iteration.
PS:参见this,例如,关于反向迭代。
PS2: I did not thoroughly tested if it handles well erasing elements at the ends.
PS2:我没有彻底测试它是否可以很好地处理末端的擦除元素。
回答by Alex Bagg
If you think of the std::list
like a queue, then you can dequeue and enqueue all the items that you want to keep, but only dequeue (and not enqueue) the item you want to remove. Here's an example where I want to remove 5 from a list containing the numbers 1-10...
如果你把它想象成std::list
一个队列,那么你可以让所有你想保留的项目出队和入队,但只能出队(而不是入队)你想要删除的项目。这是一个示例,我想从包含数字 1-10 的列表中删除 5 ...
std::list<int> myList;
int size = myList.size(); // The size needs to be saved to iterate through the whole thing
for (int i = 0; i < size; ++i)
{
int val = myList.back()
myList.pop_back() // dequeue
if (val != 5)
{
myList.push_front(val) // enqueue if not 5
}
}
myList
will now only have numbers 1-4 and 6-10.
myList
现在将只有数字 1-4 和 6-10。
回答by anand
Removal invalidates only the iterators that point to the elements that are removed.
移除只会使指向被移除元素的迭代器失效。
So in this case after removing *i , i is invalidated and you cannot do increment on it.
因此,在这种情况下,删除 *i 后, i 无效,您无法对其进行增量。
What you can do is first save the iterator of element that is to be removed , then increment the iterator and then remove the saved one.
您可以做的是首先保存要删除的元素的迭代器,然后增加迭代器,然后删除保存的迭代器。