迭代时擦除元素时出现分段错误 std::deque

Segmentation fault when erasing elements while iterating std::deque

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

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 显然不支持在遍历元素时删除元素。

  1. Why does the following code crash ? How do I erase individual elements then ?

std::deque::erase 使迭代器无效。

All iterators and references are invalidated, unless the erased elements are at the end or the beginning of the container, in which case only the iterators and references to the erased elements are invalidated.

The past-the-end iterator is also invalidated unless the erased elements are at the beginning of the container and the last element is not erased.

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

您可以利用 std::deque::erase

的 return 值

Iterator following the last removed element. If the iterator pos refers to the last element, the end() iterator is returned.

for (auto it = q.begin(); it!=q.end(); )
{
    it = q.erase(it);
}
  1. And what should I do if I am iterating via reverse iterator.

因为std::deque::erase不接受reverse_iterator作为参数,所以需要用base()转换为普通迭代器(注意位置转换)。如

for (auto it = q.rbegin(); it!=q.rend(); )
{
    it = std::make_reverse_iterator(q.erase((++it).base()));
}

根据 C++11 23.3.3.4 deque modifiers /4,如果删除某些元素,deque 迭代器将失效。

An erase operation that erases the last element of a deque invalidates only the past-the-end iterator and all iterators and references to the erased elements.

An erase operation that erases the first element of a deque but not the last element invalidates only the erased elements.

An erase operation that erases neither the first element nor the last element of a deque invalidates the past-the-end iterator and all iterators and references to all the elements of the deque.

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

it = q.erase(it);

在循环内部,因为 erase 称自己为 returns 一个“调整后的”迭代器。这也适用于删除最后一个元素。

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

q.clear();

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

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

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

q.clear();

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

这里,函数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";
}

输出为:

$ g++ test.cc -std=c++11 && ./a.out
1 2 3 4 5 
1 2 4 5 
1 2 

供参考: