【问题标题】:Removing an element from a std::set while iterating over it in C++17在 C++17 中迭代它时从 std::set 中删除一个元素
【发布时间】:2019-01-15 13:40:54
【问题描述】:

我已阅读 this SO postthis one too 关于 在迭代期间从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;
}

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

$ g++ -o main main.cpp
$ ./main
s = {4 25 300 }

在擦除这样的元素时有什么注意事项吗?谢谢。

【问题讨论】:

  • 您正在按键擦除,因此您也可以调用s.erase(-8) 并完成它。第一个 for 循环毫无意义。
  • 我认为它从 C++11 开始有效。
  • @WhozCraig 这只是一个 POC 示例...... if (it ==-8) 部分只是选择一些任意元素进行删除的示例。
  • 好的,你的 real 问题是否在 C++17 中有效的 std::set 上的 ranged-for 枚举期间使用容器修改?一旦std::set::erase 实际上开始返回一个,这个问题在 C++11 中使用迭代器解决了,但是你想知道它是否也支持 ranged-for ?我不认为是。
  • 对 C++ 的最大警告是“一切都很完美”并不意味着没有未定义的行为。 (正如 Dijkstra 所说,测试只能显示错误的存在,而不是它们的缺失。)

标签: c++ c++17 erase stdset


【解决方案1】:

根据 C++17 标准:

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

1 基于范围的 for 语句

for ( for-range-declaration : for-range-initializer ) statement

等价于

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

所以,您的代码无效,因为您删除了迭代器当前指向的元素(std::set 同一键只能有一个值!),因此迭代器得到无效并在之后递增,这是未定义的行为。

请注意,您可以从集合中删除 另一个 元素,如在std::set(以及std::mapstd::list)中,只有被删除的迭代器无效,而所有其他元素仍然有效.

如果你打算移除一个容器的当前元素(包括std::vector,因为erase返回一个新的、有效的迭代器),你需要回退到一个经典循环,如answer所示参考问题;我个人喜欢单行变体:

    iter = /*some condition*/ ? container.erase(iter) : std::next(iter);

【讨论】:

    【解决方案2】:

    如果您的 C++ 实现支持 Library Fundamentals TS(第二版),您可以这样做:

    #include <experimental/set>
    #include <iostream>
    #include <algorithm>
    #include <experimental/iterator>
    int main()
    {
        std::set<int> s;
    
        s.insert(4);
        s.insert(300);
        s.insert(25);
        s.insert(-8);
    
        std::experimental::erase_if(s,
                                    [](auto& key){ return key == -8; });
        std::cout << "s = {";
        std::copy(s.begin(), s.end(),
                  std::experimental::make_ostream_joiner(std::cout, " "));
        std::cout << "}\n";
    }
    

    【讨论】:

      猜你喜欢
      • 2011-02-21
      • 2014-01-04
      • 2011-03-22
      • 2014-09-22
      • 2011-07-13
      • 2019-03-10
      • 2010-10-10
      相关资源
      最近更新 更多