如何在c++中对向量进行切片并分配给它自己

How to slice a vector in c++ and assign to itself?

本文关键字:切片 分配 它自己 向量 c++      更新时间:2023-10-16

我想知道如何用矢量的子集替换矢量的内容,而不重新分配新矢量(我发现的每个问题似乎都有分配新矢量的目的)

示例:

vector<int>* testVec = new vector<int>{1, 2, 3, 4, 5};
//do some operation to slice vector, to say {2, 3, 4} without re-allocating
//testVec should now have contents {2, 3, 4}

关于使用范围的问题尚不清楚,但对std::vector::erase的两次调用就足够了,不会引起任何重新分配。

例如:

std::vector<int> testVec{1, 2, 3, 4, 5};
testVec.erase(testVec.begin() + 4, testVec.end());
testVec.erase(testVec.begin(), testVec.begin() + 1);

通过首先移除向量的尾部,可以确保值只移动一次。

这是完整的代码。

注意,我假设您实际上不需要使用指向std::vector的指针。

一个简单的方法是使用读/写方法:

int wp = 0; // "write ptr"
int rp = 0; // "read ptr"
for (auto& x : v) {
if (... i want to keep element x, index rp ...) {
if (rp != wp) v[wp] = std::move(x);
wp++;
}
rp++;
}
v.resize(wp);

这种方案也适用于对既依赖于索引又依赖于元素值的条件进行过滤。

您可以使用std::rotatevector::erase的组合

void slice(vector<int> & v, std::size_t start, std::size_t size)
{
assert( v.size() > start + size );
std::rotate( v.begin(), v.begin() + start, v.end() );
// Vector is now {2,3,4,5,1}
v.erase( v.begin() + size, v.end() );
}

只需移除您不想要的部件:

std::vector<int> vec{1, 2, 3, 4, 5};
vec.erase(vec.begin() + 4, vec.end());
vec.erase(vec.begin(), vec.begin() + 1);

这是一个接受谓词的小函数。

对于容器中传递谓词的每个元素,都会从容器中删除该元素。

不进行重新分配,每个元素最多为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); } );
}

现在,一种更通用的方法是创建容器的替代视图,其中视图中的每个元素都对应于原始容器中的某个元素范围,在该视图上进行测试,然后将操作应用回原始容器。

不知道如何写得简明扼要。