【问题标题】:std::multimap::erase() while iterating迭代时的 std::multimap::erase()
【发布时间】:2018-09-19 22:51:41
【问题描述】:

我有一个 std::multimap 将会话 ID (ints) 映射到该会话中使用的硬件(硬件由包含一些硬件特定信息的 struct 描述)。

我有一个清理功能,它必须对每个硬件进行特定的清理工作。清理完成后,我需要从地图中删除该元素,因为该会话中不再使用硬件。

请注意,我不只是想从会话中删除单个硬件。相反,整个会话被拆除,所以我想在地图中搜索会话 ID,清理硬件,然后从地图中删除所有这些条目。

这里有一些代码显示了我要解释的内容:

void MyClass::end_session(const int session_id) {
  // session_map_ is a member variable of MyClass
  const auto range = session_map_.equal_range(session_id);
  for (auto it = range.first; it != range.second; session_map_.erase(it++)) {
    // do cleanup for the hardware pointed to by it->second
  }
}

循环合法吗?我知道传入erase() 的迭代器会失效,但这不会使range.firstrange.second 失效,对吗?另外,session_map_.erase(it++) 是否像我期望的那样工作?也就是说,我假设it 被保存为erase() 的参数,递增到其新值,然后为旧值调用erase()(因此在递增之前使迭代器无效)。对吗?

【问题讨论】:

  • 仅供参考,std::multimap 有一个iterator-range-based erase method
  • @WhozCraig 对,但无论如何我都必须循环遍历所有元素,所以我更喜欢使用 erase() 方法,它只获取迭代器位置并在摊销的常数时间内执行操作,而不是稍后调用基于迭代器范围的方法,该方法需要 log(c.size()) + std::distance(first, last) 时间并希望我的编译器在优化时结合循环。
  • 一种更安全的session_map_.erase(it++) 方法是it = session_map_.erase(it) -- erase() 在被擦除的迭代器之后返回下一个迭代器。

标签: c++ c++11


【解决方案1】:

文档说

其他引用和迭代器不受影响

所以您的循环按预期运行,erase 按预期工作。

假设您有 4 个元素,在您的情况下,循环运行 4 次没有任何问题。

https://en.cppreference.com/w/cpp/container/multimap/erase

【讨论】:

    【解决方案2】:

    你正在做的事情是绝对安全的。

    然而,我对业务逻辑中的复杂控制流持谨慎态度。一目了然太难理解了,cmet有失步的风险。

    因此我写动词。

    template<class C, class It, class F>
    void erase_remove_if( C& c, It b, It e, F&& f) {
      auto it = b;
      while (it != e) {
        if (f(*it))
          it = c.erase(it);
        else
          ++it
      }
    }
    template<class C, class F>
    void erase_remove_if( C& c, F&& f) {
      using std::begin; using std::end;
      return erase_remove_if( c, begin(c), end(c), std::forward<F>(f) );
    }
    

    现在您的代码如下所示:

    void MyClass::end_session(const int session_id) {
      // session_map_ is a member variable of MyClass
      const auto range = session_map_.equal_range(session_id);
    
      // remove said elements after doing some processing:
      erase_remove_if( session_map_, range.first, range.second, [&](auto& kv) {
        auto&& [key, elem] = kv;
        // do cleanup for the hardware pointed to by elem
        return true; // erase element
      });
    }
    

    基本上,栅栏发布错误非常常见且难以防止,因此我宁愿将相对复杂的循环控制代码放在一个位置。

    【讨论】:

      猜你喜欢
      • 2023-03-05
      • 1970-01-01
      • 1970-01-01
      • 2015-04-30
      • 1970-01-01
      • 2012-03-11
      • 1970-01-01
      • 2016-02-20
      • 2021-08-12
      相关资源
      最近更新 更多