使用反向迭代器从向量中快速删除

时间:2019-05-18 08:23:48

标签: c++ vector stl iterator erase

我要在调用满足某些条件的向量条目后删除它们。我不在乎稳定的顺序,因此通常我实际上将最后一个数组元素替换为我正在检查的元素。

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

(是的,如果要保留顺序,“擦除-删除”是标准的习惯用法,但是在我的情况下,不需要这样做,由于这些动作,我认为它会比此处给出的版本慢。)

我将使用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上调用了delete(),我知道这将使其变得不确定,但是我没有这样做。我想pop_back()将取消定义rbegin()。我应该检查它是否在第一次迭代中进入if块,以及是否仅在这种情况下崩溃。

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

使用正向迭代器似乎可以正常工作,尽管我不喜欢在发现真正的元素时让循环停止的结巴效果。反向循环有点难看,但是至少它是真实的循环,而不是像半人马座一样的半循环半混合饮料:

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

2 个答案:

答案 0 :(得分:1)

pop_back通常应仅使back()end()无效。但是,如果必须删除数组的最后一个元素,可能会陷入困境。使用索引,没问题,您尝试在其自身上移动元素,该元素应为空操作并继续上一个索引。但是,对于迭代器,当前值为back(),因此应使其无效。

当心,这在您的实现中也可能是个问题,因此提供该信息可能很有意义,以便其他人可以尝试使用此实现或其他实现进行复制。

答案 1 :(得分:1)

我认为古老且经过测试的擦除删除惯用法很好地涵盖了这一点。

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所指出的,该算法会将要保留的元素移到最前面,考虑到它保证保留所保留元素的相对顺序,因此这是有道理的。

如果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 { ... }