【问题标题】:fast deletion from vector with a reverse iterator使用反向迭代器从向量中快速删除
【发布时间】:2019-10-05 10:10:09
【问题描述】:

我想在对它们调用函数后删除满足某些条件的向量条目。我不关心稳定的排序,所以实际上我通常会移动最后一个数组元素来替换我正在检查的那个。

问题:使用迭代器执行此操作的最巧妙的习惯用法是什么?

(是的,如果你想保留顺序,erase-remove 是标准的习惯用法,但在我的情况下,它不是必需的,而且我认为由于这些动作,它会比我在此处给出的版本慢。)

使用 int 下标我会这样做,并且此代码有效:

  for ( int i = (int) apc.size() - 1; i >= 0; i-- )
      if ( apc[i]->blah ) {
          MyFunc( apc[i] );
          apc[i] = apc.back();
          apc.pop_back();
      }

我尝试使用反向迭代器进行相同操作,但它在第一次进入 if 块后在 for 循环的 ++ 中爆炸。我不知道为什么。如果实际上在 *it 上调用了 erase(),我知道这会导致它未定义,但我没有这样做。我想 pop_back() 会取消定义 rbegin()。我应该检查它是否在第一次迭代时进入 if 块,以及它是否只在这种情况下崩溃。

  for ( auto it = apc.rbegin(); it != apc.rend(); it++ )
      if ( (*it)->blah ) {
          MyFunc( *it );
          *it = apc.back();
          apc.pop_back();
      }

使用前向迭代器,它似乎可以工作,尽管我不喜欢在查找具有 blah true 的元素时让循环停止的口吃效果。反向循环有点难看,但至少它是一个真正的循环,而不是像半人马一样的半循环半同时混合物:

  for ( auto it = apc.begin(); it != apc.end(); )
      if ( (*it)->blah ) {
          MyFunc( *it );
          *it = apc.back();
          apc.pop_back();
      } else
          it++;

【问题讨论】:

标签: c++ vector stl iterator erase


【解决方案1】:

pop_back 通常只会使back()end() 无效。但是如果必须删除数组的最后一个元素,您可能会遇到极端情况。使用索引,没问题,您尝试在其自身上移动一个元素,该元素应该是一个无操作并继续上一个索引。但是对于迭代器,当前值是back(),所以它应该是无效的。

请注意,这也可能是您的实施中的问题,因此提供该信息以便其他人可以尝试使用此实施或其他实施进行重现是有意义的。

【讨论】:

  • 我认为 pop_back() 还会使 rbegin() 无效,而你提到的极端情况肯定会让我崩溃。
【解决方案2】:

我认为旧的和经过测试的擦除删除习语很好地涵盖了这一点。

apc.erase(std::remove_if(apc.begin(), apc.end(), [](auto& v) {
    if (v->blah) {
        MyFunc(v);
        return true;
    }
    return false;
}), apc.end());

这个习语将所有要移除的元素通过std::remove_if移动到容器的末尾,然后我们用erase一次性移除所有元素。

编辑:正如 Marshall 所指出的,该算法会将要保留的元素移到前面,考虑到它承诺保留保留元素的相对顺序,这是有道理的。 p>

如果 lambda 需要作用于 this 或传入的 v 以外的任何变量,则需要捕获它。在这种情况下,我们不需要担心生命周期,因此默认的引用捕获是一个不错的选择。

[&](auto& v) {
    if (v->blah < x) { //captures x by reference
        MyFunc(v, member_variable); //current object is captured by reference, and can access member variables
        return true;
    }
    return false;
})

如果 MyFunc 可能会修改 member_variable,我们还需要使 lambda 可变。

默认情况下,lambda 创建一个带有operator() const 的函数对象,但mutable 删除了const

[&](auto& v) mutable { ... }

【讨论】:

  • 好的,感觉很接近!我的“blah”实际上是一个比较 v->blah
  • @SwissFrank 您可以通过在 lambda 开头使用 [&] 而不是 [] 通过引用指定默认捕获。
  • 这段代码是正确的,但解释不太对。 remove_if 不会“将所有要删除的元素移动到容器的末尾”。相反,它“将所有要保留的元素移动到容器的开头”,并将任何内容留在容器的末尾。
  • @MarshallClow 这是有道理的,因为它承诺保留保留元素的相对顺序。不过严格来说,我认为这是定义的实现。我会在答案中添加注释。
猜你喜欢
  • 1970-01-01
  • 2021-07-03
  • 1970-01-01
  • 1970-01-01
  • 2021-11-08
  • 2022-01-20
相关资源
最近更新 更多