Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the most effective way to move items within a vector?

Tags:

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 and wants to code function like:

void move( int from, int count, int to, std::vector<int>& numbers );

I've been thinking about creation of a new vector + std::copy or combination of insert/erase but I can't say I ended up with some nice and elegant solution.

like image 267
Miro Kropacek Avatar asked Sep 23 '11 10:09

Miro Kropacek


People also ask

How do you move a vector element?

You can't move elements from one vector to another the way you are thinking about; you will always have to erase the element positions from the first vector. If you want to change all the elements from the first vector into the second and vice versa you can use swap. @R.

Can vector be moved?

Yes, you can move vectors. Vector is fully defined by it's components in some basis. Not by "components and point of origin". It's just a matter of how mathematicians defined what is "vector".

What happens when you move a vector?

Does move changes the capacity of the vector? std::move is just a cast. It does nothing to the object. It onky changes the type of the reference.

How do you move an element in C++?

std::move in C++ Moves the elements in the range [first,last] into the range beginning at result. The value of the elements in the [first,last] is transferred to the elements pointed by result. After the call, the elements in the range [first,last] are left in an unspecified but valid state.


2 Answers

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.)

like image 157
Kerrek SB Avatar answered Oct 12 '22 08:10

Kerrek SB


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.)

like image 34
Blastfurnace Avatar answered Oct 12 '22 07:10

Blastfurnace