问题
I would like to know how I can replace the contents of a vector with a subset of that vector, without re-allocating a new vector (every question I have found appears to have the purpose of allocating a new vector)
example:
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}
回答1:
The question is not clear on the usage scope, but two calls to std::vector::erase
would suffice and don't incur any reallocation.
For your example:
std::vector<int> testVec{1, 2, 3, 4, 5};
testVec.erase(testVec.begin() + 4, testVec.end());
testVec.erase(testVec.begin(), testVec.begin() + 1);
By first removing the tail of the vector you can make sure the values are only moved once.
Here is the full code.
Note that I assumed that you don't actually need to use a pointer to std::vector
.
回答2:
A simple way is to use a read/write approach:
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);
this kind of scheme works also for filtering on conditions that depend both on the index and on the element value.
回答3:
You can use a combination of std::rotate
and vector::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() );
}
回答4:
Just remove the parts you don't want:
std::vector<int> vec{1, 2, 3, 4, 5};
vec.erase(vec.begin() + 4, vec.end());
vec.erase(vec.begin(), vec.begin() + 1);
回答5:
This is a little function that takes a predicate.
For every element of the container that passes the predicate, the element is erased from the container.
No reallocation is done, and each element is std::move
'd at most once:
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) );
}
Doing this with indexes is a bit tricky. If the indexes are contiguous, you can just rotate the first one into the first position, then erase the tail end of the container, or erase the tail and the front, or a number of myriad ways.
If they are not, you basically have to write a manual version of std::remove
that works on indexes instead of on element values.
It would probably work if you had a "counting" test function that kept count of which elements it was called on, but relying on the order that the test function is called on seems overly fragile. And remove_by_index
is easy enough:
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;
}
giving us:
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) );
}
Suppose you want to keep the slice of all even position elements:
erase_by_index( vec, [](auto i){return i&1;} );
or suppose we want to keep an interval:
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); } );
}
Now a more generic way would consist of creating an alternative view of your container, where each element in the view corresponds to some range of elements in your original container, doing tests on that view, then applying the operation back on the original container.
Not sure how to write that succinctly.
来源:https://stackoverflow.com/questions/37575163/how-to-slice-a-vector-in-c-and-assign-to-itself