C++ 迭代std::deque时删除元素时出现分段错误

C++ 迭代std::deque时删除元素时出现分段错误,c++,iterator,deque,erase,C++,Iterator,Deque,Erase,为什么下面的代码会崩溃?当我通过反向迭代器进行迭代时应该怎么做。那么如何擦除单个元素呢 deque q; q.push_back(4); q.push_back(41); q.push_back(14); for (auto it = q.begin(); it != q.end();) { auto current = it; q.erase(current); it++; } q显然不支持在遍历元素时删除元素 为什么下面的代码会崩溃?那么如何擦除单个元素呢 使迭

为什么下面的代码会崩溃?当我通过反向迭代器进行迭代时应该怎么做。那么如何擦除单个元素呢

deque q;
q.push_back(4);
q.push_back(41);
q.push_back(14);

for (auto it = q.begin(); it != q.end();) {
    auto current = it;
    q.erase(current);
    it++; 
}

q显然不支持在遍历元素时删除元素

  • 为什么下面的代码会崩溃?那么如何擦除单个元素呢
  • 使迭代器无效

    所有迭代器和引用均无效,除非已擦除的元素位于容器的末尾或开头,在这种情况下,只有迭代器和对已擦除元素的引用无效

    结束迭代器之后的元素也将无效,除非已擦除的元素位于容器的开头,并且最后一个元素未被擦除

    在您的代码中,要擦除的元素的迭代器(即
    it
    current
    )在
    q.erase(current)
    之后将变得无效,然后
    it++
    将导致UB

    您可以使用
    std::deque::erase的返回值

    最后删除的元素后面的迭代器。如果迭代器pos引用最后一个元素,则返回
    end()
    迭代器

  • 如果我通过反向迭代器进行迭代,我应该怎么做
  • 由于
    std::deque::erase
    不接受
    reverse_iterator
    作为参数,因此需要使用将其转换为普通迭代器(注意位置转换)。比如


    根据
    C++11 23.3.3.4 deque修饰符/4
    deque
    迭代器在删除某些元素时无效

    擦除数据块的最后一个元素的擦除操作仅使结束迭代器和所有迭代器以及对已擦除元素的引用失效

    擦除数据块的第一个元素而不是最后一个元素的一种擦除操作,只使被擦除的元素失效

    一种擦除操作,既不擦除deque的第一个元素,也不擦除deque的最后一个元素,将使过去的结束迭代器和所有迭代器以及对deque所有元素的引用无效

    在您的情况下,您通常只删除第一个元素,因此它只会使该元素无效。这意味着
    it++
    是无效的,您应该改为使用如下内容:

    it = q.erase(it);
    
    在循环内部,因为
    erase
    调用本身返回一个“调整的”迭代器。删除最后一个元素时,这也会起作用

    但是,由于您的代码完全清除了列表(假设它不是需要处理每个元素的精简版本),因此您可以完全放弃循环,只需使用:

    q.clear();
    

    正如其他回答者已经指出的,从队列中删除元素将使用于迭代其元素的迭代器无效。因此它失败了

    但我假设您不打算删除队列中的所有元素,在这种情况下,您可能更愿意使用:

    q.erase(q.begin(), q.end());
    

    因此,我建议使用另一种技术,可用于从队列中删除符合特定条件的项:删除

    这里,函数
    std::remove(…)
    std::remove_if(…)
    用于将要删除的项目(符合某些条件)移动到容器的末尾。然后使用基于范围的
    q.erase(…)
    版本删除项目

    下面是一个例子:

    #include <deque>
    #include <algorithm>
    #include <iostream>
    
    // predicate function for removal of elements
    bool greater_three(int x) {
        return x > 3;
    }
    
    int main() {
        std::deque<int> q = {1,2,3,4,5};
        for (auto i : q) std::cout << i << " "; std::cout << "\n";
        // delete all items with value 3
        q.erase(std::remove(q.begin(), q.end(), 3), q.end()); 
        for (auto i : q) std::cout << i << " "; std::cout << "\n";
        // delete all items with value > 3
        q.erase(std::remove_if(q.begin(), q.end(), greater_three), q.end()); 
        for (auto i : q) std::cout << i << " "; std::cout << "\n";
    }
    
    供参考:


    您是否尝试过将标准库切换到诊断或调试模式?它可能会告诉您使用的迭代器无效。答案太棒了!这帮了大忙。因此,如果我需要在deque中存储一个我希望稍后访问的位置,我该怎么做?在存储和访问之间,某些元素也可能被擦除。@Ram您必须自己处理,即确保迭代器在使用时仍然有效。回答得很好!这帮了大忙。因此,如果我需要在deque中存储一个我希望稍后访问的位置,我该怎么做?在存储和访问之间,某些元素也可能被擦除。@Ram,如果在保存和使用之间修改集合,则保存迭代器“位置”将没有帮助-该位置将无效。如果集合中的项在某种程度上是唯一的,则可以存储项值本身而不是位置,然后在列表上迭代查找该项并将其删除。
    q.erase(q.begin(), q.end());
    
    q.clear();
    
    #include <deque>
    #include <algorithm>
    #include <iostream>
    
    // predicate function for removal of elements
    bool greater_three(int x) {
        return x > 3;
    }
    
    int main() {
        std::deque<int> q = {1,2,3,4,5};
        for (auto i : q) std::cout << i << " "; std::cout << "\n";
        // delete all items with value 3
        q.erase(std::remove(q.begin(), q.end(), 3), q.end()); 
        for (auto i : q) std::cout << i << " "; std::cout << "\n";
        // delete all items with value > 3
        q.erase(std::remove_if(q.begin(), q.end(), greater_three), q.end()); 
        for (auto i : q) std::cout << i << " "; std::cout << "\n";
    }
    
    $ g++ test.cc -std=c++11 && ./a.out
    1 2 3 4 5 
    1 2 4 5 
    1 2