在C++ 17中迭代它时,从std :: set中删除一个元素

Ore*_*lom 7 c++ erase stdset c++17

我已经读过这篇SO帖子了,这个也是关于std::set迭代过程中元素的擦除.但是,似乎C++ 17中存在一个更简单的解决方案:

#include <set>
#include <iostream>
int main(int argc,char **argv)
{
    std::set<int> s;

    s.insert(4);
    s.insert(300);
    s.insert(25);
    s.insert(-8);

    for (auto it:s)
    {
        if (it == -8)
        {
            s.erase(it);
        }
    }
    std::cout << "s = {";
    for (auto it:s)
    {
        std::cout << it << " ";
    }
    std::cout << "}\n";
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

当我编译并运行它时,一切都很完美:

$ g++ -o main main.cpp
$ ./main
s = {4 25 300 }
Run Code Online (Sandbox Code Playgroud)

擦除这样的元素有什么警告吗?谢谢.

Aco*_*gua 12

根据C++ 17标准:

9.5.4基于范围的语句[stmt.ranged]

1基于范围的声明

for ( for-range-declaration : for-range-initializer ) statement
Run Code Online (Sandbox Code Playgroud)

相当于

{
    auto &&__range = for-range-initializer ;
    auto __begin = begin-expr ;
    auto __end = end-expr ;
    for ( ; __begin != __end; ++__begin )
    {
        for-range-declaration = *__begin;
        statement
    }
}
Run Code Online (Sandbox Code Playgroud)

所以没有,你的代码是无效的,因为你抹去迭代器当前指向的元素(std::set只能有一个相同的密钥值!),从而得到迭代器失效和递增之后,这是不确定的行为.

要知道,你可以删除另一个从集合元素,如std::set(以及在std::mapstd::list)删除只迭代器失效,而所有其他仍然有效.

如果您打算删除容器的当前元素(包括std::vector,erase返回一个新的,有效的迭代器),您需要回退到经典循环,如引用问题的答案所示; 我个人喜欢一个单行的变体:

    iter = /*some condition*/ ? container.erase(iter) : std::next(iter);
Run Code Online (Sandbox Code Playgroud)