我要在调用满足某些条件的向量条目后删除它们。我不在乎稳定的顺序,因此通常我实际上将最后一个数组元素替换为我正在检查的元素。
问题:使用迭代器进行此操作最巧妙的习惯是什么?
(是的,如果要保留顺序,“擦除-删除”是标准的习惯用法,但是在我的情况下,不需要这样做,由于这些动作,我认为它会比此处给出的版本慢。)
我将使用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++;
答案 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 { ... }