272

我的代码如下所示:

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++:“List iterator not incrementable”时会出现错误。我尝试了一些在 for 语句中没有增加的替代方法,但是我什么也做不了。

走 std::list 时删除项目的最佳方法是什么?

4

14 回答 14

320

您必须首先增加迭代器(使用 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;
    }
}
于 2009-02-27T19:11:26.067 回答
151

你想做:

i= items.erase(i);

这将正确更新迭代器以指向您删除的迭代器之后的位置。

于 2009-02-27T19:18:02.133 回答
25

您需要结合 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);
    }
}

当然,最高效和 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)));
于 2009-02-27T21:12:11.407 回答
10

使用std::remove_if算法。

编辑:
使用集合应该是这样的:

  1. 准备收藏。
  2. 过程采集。

如果您不混合这些步骤,生活会更轻松。

  1. std::remove_if. 或list::remove_if(如果您知道您使用的是 list 而不是 the TCollection
  2. std::for_each
于 2009-02-27T19:29:32.947 回答
9

我已经总结了,这里是三个方法的例子:

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.使用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.使用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.使用for循环,要注意更新迭代器:

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 
于 2018-10-24T01:24:01.437 回答
5

克里斯托答案的替代循环版本。

你失去了一些效率,你在删除时后退然后再次前进,但是为了换取额外的迭代器增量,你可以在循环范围内声明迭代器并且代码看起来更干净。选择什么取决于当下的优先事项。

答案完全过时了,我知道...

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);
    }
}
于 2011-08-05T12:57:59.763 回答
5

这是一个使用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);
于 2013-07-04T20:37:39.527 回答
2

删除仅使指向已删除元素的迭代器无效。

因此,在这种情况下,删除 *i 后, i 无效,您无法对其进行增量。

您可以做的是首先保存要删除的元素的迭代器,然后递增迭代器,然后删除保存的迭代器。

于 2009-02-27T19:29:58.580 回答
2

如果您将其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现在只有数字 1-4 和 6-10。

于 2015-06-03T23:55:26.260 回答
2

向后迭代避免了擦除元素对要遍历的剩余元素的影响:

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:参见this,例如,关于反向迭代。

PS2:我没有彻底测试它是否能很好地处理末端的擦除元素。

于 2019-03-13T04:52:05.660 回答
1

你可以写

std::list<item*>::iterator i = items.begin();
while (i != items.end())
{
    bool isActive = (*i)->update();
    if (!isActive) {
        i = items.erase(i); 
    } else {
        other_code_involving(*i);
        i++;
    }
}

您可以使用 编写等效代码std::list::remove_if,这样更简洁且更明确

items.remove_if([] (item*i) {
    bool isActive = (*i)->update();
    if (!isActive) 
        return true;

    other_code_involving(*i);
    return false;
});

std::vector::erase std::remove_if当 items 是一个向量而不是一个列表以保持 O(n) 的复杂性时,应该使用该习语 - 或者如果您编写通用代码并且 items 可能是一个没有有效方法擦除单个项目的容器(如向量)

items.erase(std::remove_if(begin(items), end(items), [] (item*i) {
    bool isActive = (*i)->update();
    if (!isActive) 
        return true;

    other_code_involving(*i);
    return false;
}));
于 2016-03-26T09:06:31.153 回答
0

做while循环,灵活、快速、易读易写。

auto textRegion = m_pdfTextRegions.begin();
    while(textRegion != m_pdfTextRegions.end())
    {
        if ((*textRegion)->glyphs.empty())
        {
            m_pdfTextRegions.erase(textRegion);
            textRegion = m_pdfTextRegions.begin();
        }
        else
            textRegion++;
    } 
于 2020-07-12T19:16:13.250 回答
0

我想分享我的方法。此方法还允许在迭代期间将元素插入到列表的末尾

#include <iostream>
#include <list>

int main(int argc, char **argv) {
  std::list<int> d;
  for (int i = 0; i < 12; ++i) {
    d.push_back(i);
  }

  auto it = d.begin();
  int nelem = d.size(); // number of current elements
  for (int ielem = 0; ielem < nelem; ++ielem) {
    auto &i = *it;
    if (i % 2 == 0) {
      it = d.erase(it);
    } else {
      if (i % 3 == 0) {
        d.push_back(3*i);
      }
      ++it;
    }
  }

  for (auto i : d) {
      std::cout << i << ", ";
  }
  std::cout << std::endl;
  // result should be: 1, 3, 5, 7, 9, 11, 9, 27,
  return 0;
}
于 2021-05-08T05:37:47.883 回答
-4

我认为你有一个错误,我这样编码:

for (std::list<CAudioChannel *>::iterator itAudioChannel = audioChannels.begin();
             itAudioChannel != audioChannels.end(); )
{
    CAudioChannel *audioChannel = *itAudioChannel;
    std::list<CAudioChannel *>::iterator itCurrentAudioChannel = itAudioChannel;
    itAudioChannel++;

    if (audioChannel->destroyMe)
    {
        audioChannels.erase(itCurrentAudioChannel);
        delete audioChannel;
        continue;
    }
    audioChannel->Mix(outBuffer, numSamples);
}
于 2012-09-18T16:15:53.900 回答