I\'ve seen some special cases where std::rotate
could be used or a combination with one of the search algorithms but generally: when one has a vector of N items
Depending on the size of the vector and the ranges involved, this might be less expensive than performing copy/erase/insert.
template <typename T>
void move_range(size_t start, size_t length, size_t dst, std::vector<T> & v)
{
typename std::vector<T>::iterator first, middle, last;
if (start < dst)
{
first = v.begin() + start;
middle = first + length;
last = v.begin() + dst;
}
else
{
first = v.begin() + dst;
middle = v.begin() + start;
last = middle + length;
}
std::rotate(first, middle, last);
}
(This assumes the ranges are valid and they don't overlap.)
Pre-C++11 (although the following remains valid) you can get more efficient "moves" for contained types which specialise/overload std::swap
. To take advantage of this, you would need to do something like
std::vector<Foo> new_vec;
Foo tmp;
for (/* each Foo&f in old_vec, first section */) {
swap (f, tmp);
new_vec .push_back (tmp);
}
for (/* each Foo&f in old_vec, second section */) {
swap (f, tmp);
new_vec .push_back (tmp);
}
for (/* each Foo&f in old_vec, third section */) {
swap (f, tmp);
new_vec .push_back (tmp);
}
swap (new_vec, old_vec);
The above may also give good results for C++11 if Foo has a move-operator but hasn't specialised swap
.
Linked lists or some clever sequence type might work out better if Foo
doesn't have move semantics or an otherwise-optimised swap
Note also that if the above is in a function
std::vector<Foo> move (std::vector<Foo> old_vec, ...)`
then you might be able to perform the whole operation without copying anything, even in C++98 but for this to work you will need to pass by value and not by reference, which goes against the conventional prefer-pass-by-reference wisdom.
It's always important to profile before jumping to any conclusions. The contiguity of vector
's data memory may offer significant caching benefits that node-based containers don't. So, perhaps you could give the direct approach a try:
void move_range(size_t start, size_t length, size_t dst, std::vector<T> & v)
{
const size_t final_dst = dst > start ? dst - length : dst;
std::vector<T> tmp(v.begin() + start, v.begin() + start + length);
v.erase(v.begin() + start, v.begin() + start + length);
v.insert(v.begin() + final_dst, tmp.begin(), tmp.end());
}
In C++11, you'd wrap the iterators in the first and third line into std::make_move_iterator
.
(The requirement is that dst
not lie within [start, start + length)
, or otherwise the problem is not well-defined.)