【问题标题】:C++: using an iterator of dense_hash_set after erase(*it)C ++:在擦除(* it)之后使用dense_hash_set的迭代器
【发布时间】:2019-01-29 14:47:41
【问题描述】:

我有this C++ code file 这是FOSS 根据外籍人士许可证。运行时cppcheck 在代码上我得到这个错误:

[google_hash.cpp:137] -> [google_hash.cpp:141]: (error) Iterator 'it' used after element has been erased.

有问题的代码是:

    74  #if (FCS_WHICH_STATES_GOOGLE_HASH == FCS_WHICH_STATES_GOOGLE_HASH__SPARSE)
    75  typedef sparse_hash_set<char *, state_hash, state_equality> StatesGoogleHash;
    76  #else
    77  typedef dense_hash_set<char *, state_hash, state_equality> StatesGoogleHash;
    78  #endif
.
.
.
   131  extern void fc_solve_states_google_hash_foreach(
   132      fcs_states_google_hash_handle void_hash,
   133      bool (*should_delete_ptr)(void *key, void *context), void *context)
   134  {
   135      StatesGoogleHash *hash = (StatesGoogleHash *)void_hash;
   136
   137      for (StatesGoogleHash::iterator it = hash->begin(); it != hash->end(); ++it)
   138      {
   139          if (should_delete_ptr(*(it), context))
   140          {
   141              hash->erase(it);
   142          }
   143      }
   144  }

它利用了 http://goog-sparsehash.sourceforge.net/doc/sparse_hash_set.htmlhttp://goog-sparsehash.sourceforge.net/doc/dense_hash_set.html

现在,这些文件说

Validity of Iterators

insert() invalidates all iterators, as does resize().
erase() is guaranteed not to invalidate any iterators.

所以我的问题是 - 我是否正确且安全地使用集合的迭代器 (并且 cppcheck 发出了误报),或者如果不是 - 应该如何 代码是固定的?

我们将不胜感激。

【问题讨论】:

  • 根据文档,您似乎是。 ithash 上的迭代器,而 hash-&gt;erase(it); 据说不会使迭代器失效,因此不应使 it 失效。尽管文档可能不正确,这意味着对其他元素的迭代器无效。或者也许有一种期望,暗示it 不算作“任何迭代器”的一部分。请注意,erase 标有注释 6,该注释似乎从文档中丢失,可能是为了涵盖这一点。
  • 即使erase() 不会使it 失效,在你删除它的元素之后it++ 的用途还不清楚。它绝对不是容器元素的迭代器,那么它代表什么?它是否隐式引用it 之后的元素?之前的那个?无论哪种情况,我都认为说它没有失效是不正确的。它当然不能仍然指同一件事。如果它根本不引用特定元素,那么它支持哪些操作?这样的迭代器确实需要它自己的类别定义。
  • 对于自称为容器的东西来说,这是一个奇怪的接口。我希望 erase(iterator) 函数返回下一个迭代器,而不是 void
  • @FrançoisAndrieux 有一个不能取消引用的 one 迭代器值是很正常的,end 迭代器。似乎这个 api 可以有多个有效但不可取消引用的值。
  • @Caleth 这是一个很好的观点。但它是一个范围内唯一不能取消引用的迭代器值,并且假设用户可以理解过去的迭代器的概念,因为它类似于过去的结束的标准概念指针。 beginend 的特殊之处还在于它们定义了一个范围。同样,假设开发人员会理解对它们的限制是相对安全的,因为如果用户理解迭代器,他们就必须理解范围。这个有效的非end 但不可引用的迭代器没有标准的先例并且需要文档。

标签: c++ sparsehash


【解决方案1】:

这是一个误报,因为 cppcheck 认为在 iterator 上调用 erase 的 API 会使迭代器无效。这是它使用的规则,因为这是一个明智的 API。

那么对于这段代码,这样做似乎是有效的 ++it 因为这个:

这是通过使 erase() 不调整哈希表大小来实现的。如果您希望获得最大的空间效率,您可以在一串 erase() 调用之后调用 resize(0),以强制哈希表将大小调整为可能的最小大小。

这似乎意味着如果您在擦除之间不调用resize(0),迭代器可能会遇到已删除的对象。在这里很好,因为擦除是在同一个循环中完成的。

这段代码通过不显眼来隐藏其行为并且不使用标准模式这一事实无济于事。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-02
    相关资源
    最近更新 更多