如何在修改容器时正确迭代容器?

luo*_*o23 3 c++ iterator loops

我想在迭代时修改我的容器。但是代码的第一个版本不能正常工作。有人可以解释原因吗?

// 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我尝试了另一个版本。好像没问题。当我这样迭代时有什么潜在的问题吗?

// 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());
    }

}
Run Code Online (Sandbox Code Playgroud)

eer*_*ika 6

看看文档std::set::erase

对被擦除元素的引用和迭代器无效。

这应该不足为奇。

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

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

注意当前元素的迭代器在迭代后是如何通过循环增量使用的。增加无效迭代器的行为是未定义的。

在第二个循环中,您总是通过调用 获得一个有效的迭代器std::set::begin


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

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