在修改容器时如何正确地遍历容器?

时间:2020-01-09 15:21:01

标签: c++ loops iterator

我想在遍历它的同时修改我的容器。但是该代码的第一个版本无法正常运行。有人可以解释原因吗?

// version 1
int main(){
    int a=1, b=2, c=5;
    std::set<int*> m = {&a, &b, &c};

    for(auto mi : m){
        std::cout << *mi << std::endl;
        m.erase(mi);
    }
}

然后我尝试了另一个版本。好像还好这样迭代是否有潜在的麻烦?

// version 2
int main(){
    int a=1, b=2, c=5;
    std::set<int*> m = {&a, &b, &c};

    while (!m.empty()){
        std::cout << **m.begin() << std::endl;
        m.erase(m.begin());
    }

}

2 个答案:

答案 0 :(得分:5)

看看std::set::erase的文档:

对已删除元素的引用和迭代器无效。

这不足为奇。

现在,考虑一下基于范围的for循环的简写:

auto && range = range_expression ;
for (auto begin = begin_expr, end = end_expr; begin != end; ++begin) {
    range_declaration = *begin;
    loop_statement
}

通过循环增量,通知迭代后如何使用当前元素的迭代器。递增无效迭代器的行为是不确定的。

在第二个循环中,始终通过调用std::set::begin获得有效的迭代器。


在这种特殊情况下,在迭代时修改容器是没有意义的。编写一个循环以打印所有元素,然后调用clear获得相同的结果。

通常,在某些特定情况下,例如上文所述,有一种更简单的方法,但是如果没有,通常,在迭代时修改容器的正确方法是不使用range-for而是使用显式迭代器循环并使用操作结果(擦除或插入)作为下一个迭代器值,而不是递增现在无效的迭代器。

答案 1 :(得分:1)

第二种解决方案没有发现任何问题,不过您可以使用set iterator

#include <iostream>
#include <set>

using namespace std;

int main(){
    int a=1, b=2, c=5;
    set<int*> m = {&a, &b, &c};
    set<int*>::iterator it = m.begin();

    while(it != m.end()){
        cout << **it << endl;
        it = m.erase(it);
    }
}
相关问题