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

前端 未结 3 1321
别跟我提以往
别跟我提以往 2020-12-29 05:46

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

相关标签:
3条回答
  • 2020-12-29 06:15

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

    0 讨论(0)
  • 2020-12-29 06:29

    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.

    0 讨论(0)
  • 2020-12-29 06:40

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

    0 讨论(0)
提交回复
热议问题