这是一个带有谓词的小函数。
对于传递谓词的容器中的每个元素,该元素都会从容器中删除。
没有重新分配,每个元素最多std::move'd一次:
template<class C, class F>
void erase_if( C& c, F&& f ) {
using std::begin; using std::end;
auto it = std::remove( begin(c), end(c), std::forward<F>(f) );
c.erase( it, end(c) );
}
使用索引执行此操作有点棘手。如果索引是连续的,您可以将第一个旋转到第一个位置,然后擦除容器的尾部,或者擦除尾部和前端,或者多种方式。
如果不是,您基本上必须编写std::remove 的手动版本,它适用于索引而不是元素值。
如果你有一个“计数”测试函数来计算它被调用的元素,它可能会起作用,但是依赖于调用测试函数的顺序似乎过于脆弱。 remove_by_index 很简单:
template<class Begin, class End, class Test>
void remove_by_index(Begin b, End e, Test t) {
auto writer = b;
auto reader = b;
std::size_t index = 0;
while (reader != e) {
if (t(index)) {
++reader; ++index;
continue;
}
if (reader != writer) {
*writer = std::move(*reader);
}
++reader; ++writer; ++index;
continue;
}
return writer;
}
给我们:
template<class C, class F>
void erase_by_index( C& c, F&& f ) {
using std::begin; using std::end;
auto it = remove_by_index( begin(c), end(c), std::forward<F>(f) );
c.erase( it, end(c) );
}
假设你想保留所有偶数位置元素的切片:
erase_by_index( vec, [](auto i){return i&1;} );
或者假设我们想要保持一个区间:
template<class C>
void keep_interval( C& c, std::size_t start_index, std::size_t length ) {
erase_by_index(c, [=](auto i){ return i < start_index || i >= (start_index+length); } );
}
现在更通用的方法是创建容器的替代视图,其中视图中的每个元素对应于原始容器中的某些元素范围,在该视图上进行测试,然后将操作应用回原始容器容器。
不知道如何简洁。