【问题标题】:How to remove from a list inside a for loop in c++如何从c ++中for循环内的列表中删除
【发布时间】:2021-09-26 19:50:56
【问题描述】:

有没有办法在循环遍历该列表的 for 循环中删除列表项? 示例:

std::list<int> myList;
myList.push_back(5);
myList.push_back(8);

std::list<int>::iterator i;

for (i = myList.begin(); i != myList.end(); i++)
{
    if (i == 8)
        // myList.remove(*i);
}

有什么办法可以用其他东西替换myList.remove(*i),因为那样会报错。

【问题讨论】:

  • i == 8 不会编译。看看 std::list::erase。
  • 我以“i == 5”为例
  • 因为这会报错。 -- 请发布错误。
  • 有没有什么方法可以删除循环遍历该列表的 for 循环中的列表项? -- 您真正想要做什么?从列表中删除所有符合特定条件的项目?您不需要循环来执行此操作。
  • i 是迭代器,例如指针,所以我想取消引用以比较值if (*i == 8)...

标签: c++ list


【解决方案1】:

首先:如果遍历整个列表只是为了删除所需的项目,只需使用 list::remove 而不使用循环。它会为您解决问题。

但是,当出于这个或其他原因需要循环时,list::erase 是可行的方法,但它需要手动调整迭代器: 看: https://stackoverflow.com/a/596180/4885321 常规 for 循环不会按预期工作,因为(前面有错误代码):

for(auto i = l.begin();
  i!=l.end();
  ++i) //2nd iteration starts, we're incrementing non-existing iterator, UB!
{
  if((*i) == myVal) //let's assume it's true for the first element
    l.erase(i);  //ok, erase it
}

因此,正确的解决方案应如下所示

while (i != l.end()) {
    if (*i == myVal) {
        l.erase(i++);
        // or i = l.erase(i);
        // note: valid iterator is incremented before call to erase thus the new i is valid
        // the node pointed to by the old one is removed
    } else {
        ++i;
    }
}

我建议在 Meyers 的 Effective STL 中查找有关该主题和相关主题的更多信息。

【讨论】:

  • 我个人仍然更喜欢 for 循环,以将 i 的范围限制为恰好这个循环(即使问题中也没有完成):for(auto i = l.begin(); i != l.end();) 之后执行空表达式循环。
  • @SanduChicu 请注意,这个算法适用于std::list,就像你的问题一样,如果你切换到std::vector,效率会很低,因为每次擦除所有后续元素都会被复制到一个位置正面。在那种的情况下,你需要另一种算法。
  • @Aconcagua 当然你的建议也是正确的,但对我来说,for循环应该有自己的增量指令,我想避免像for (auto i = l.begin(), e=l.end(); i!=e; i = (*i==myVal) ? l.erase(i) : next(i));这样的怪物,但很难在风格上争论,尤其是在脱离上下文的演示代码中。
【解决方案2】:

要擦除所有等于 8 的项目,只需使用擦除/删除习语。无需编写任何循环:

#include <list>
#include <algorithm>
#include <iostream>

int main()
{
    std::list<int> myList;
    myList.push_back(5);
    myList.push_back(8);
    std::cout << "Before:\n";
    for (auto i : myList)
       std::cout << i << "\n";

    // Erase all the items that equal 8
    myList.erase(std::remove(myList.begin(), myList.end(), 8), myList.end());    

    std::cout << "\nAfter:\n";
    for (auto i : myList)
       std::cout << i << "\n";
}

输出:

Before:
5
8

After:
5

   

【讨论】:

  • 这行得通,并且具有使其与矢量可互换的好处(不确定其他容器)。虽然对于列表只使用擦除删除似乎是多余的。
【解决方案3】:

您正在使用iterator,所以有一个方法erase。你可以像这样使用它

while (i != myList.end())
{
    if (*i == 8) // dereferance the i
       i = myList.erase(i);
    else i++;
}

【讨论】:

  • 您的循环将(可能)增加已擦除的迭代器。
  • 那么你知道如何解决这个问题吗?我对 C++ 有点陌生
  • 您确定不要i = myList.erase(i);en.cppreference.com/w/cpp/container/list/erase
  • 还有一个问题:如果列表中只有一项,会报错:"Expression: cannot increment end list iterator"
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-16
  • 2021-10-12
  • 1970-01-01
  • 2021-12-10
  • 2019-07-10
相关资源
最近更新 更多