93

How can I move some elements from first vector to second, and the elements will remove from the first?
if I am using std::move, the elements not removed from first vector.
this is the code I wrote:

   move(xSpaces1.begin() + 7, xSpaces1.end(), back_inserter(xSpaces2));
user1544067
  • 1,676
  • 2
  • 19
  • 30

4 Answers4

107

Resurrecting an old thread, but I am surprised that nobody mentioned std::make_move_iterator combined with insert. It has the important performance benefit of preallocating space in the target vector:

v2.insert(v2.end(), std::make_move_iterator(v1.begin() + 7), 
                    std::make_move_iterator(v1.end()));

As others have pointed out, first vector v1 is now in indeterminate state, so use erase to clear the mess:

v1.erase(v1.begin() + 7, v1.end());
Gaurav Pant
  • 962
  • 10
  • 30
Miljen Mikic
  • 14,765
  • 8
  • 58
  • 66
  • You think it might be possible to optimize this further if the subvector to be inserted is an r-value? (returned from a function, only purpose is to be added to the bigger vector) – Bar Sep 11 '18 at 18:39
  • @Bar `std::make_move_iterator` produces `std::move_iterator` whose purpose is to convert the value returned by the underlying iterator into an rvalue. If the subvector is already an rvalue, I don't think it brings the additional optimization. – Miljen Mikic Sep 14 '18 at 12:26
  • 1
    A useful extension of this is that if you want to construct a new vector containing only the subset of items being removed from the original vector, you can do it at construction time, with `std::vector v2(std::make_move_iterator(v1.begin() + 7), std::make_move_iterator(v1.end()));` immediately followed by the matching `v1.erase`. And, of course, the initial iterator could be the result of `std::remove_if`, to extract all items matching a predicate. – Miral May 16 '19 at 02:11
  • @MiljenMikic Could you point to any documentation or any source that proves that "It has important performance benefit of preallocating space in the target vector"? – Mandeep Singh Dec 11 '19 at 15:59
  • 2
    @MandeepSingh See http://www.cplusplus.com/reference/vector/vector/insert/, specifically the complexity part: `Linear on the number of elements inserted (copy/move construction) plus the number of elements after position (moving). Additionally, if InputIterator in the range insert (3) is not at least of a forward iterator category (i.e., just an input iterator) the new capacity cannot be determined beforehand and the insertion incurs in additional logarithmic complexity in size (reallocations).` However, `v1.begin()` returns random access iterator, so the new capacity *can* be determined. – Miljen Mikic Dec 11 '19 at 17:01
  • The important question is whether this avoids allocating more memory than needed. Judging by "_the important performance benefit of preallocating_", it doesn't avoid. That is, it first allocates new chunk of memory (if needed), copies elements from `v1` to `v2`, and then leaves `v1` in indeterminate state (until `v1.erase` call). Correct? – gsarret Jan 26 '22 at 01:55
  • @gsarret It probably allocates a bit more than needed (this is an implementation detail, though), but the point with preallocating is that there is no reallocating happening as the new elements are being moved into the target vector. Of course, this speeds up the whole thing dramatically. It is pretty much the same behavior as if you are inserting many elements into a `std::vector` instance with and without calling the `reserve` function beforehand; the performance difference is enormous. – Miljen Mikic Jan 27 '22 at 08:07
  • @miljen-mikic yes, thank you! I understand that. I was wondering if there's a way to move elements from one `vector` to the other _without_ allocating additional memory. But I believe this is impossible because of the contiguous-memory requirement. In that case, a list would more suitable, I guess. – gsarret Feb 02 '22 at 21:08
  • 1
    @gsarret Yes, vector internally works as a resizable array, and it keeps data in contiguous memory, so this is impossible. – Miljen Mikic Feb 04 '22 at 10:52
72

std::move and std::copy operate on elements, not containers. You have to mutate the container separately. For example, to move the first 17 elements of v1 into a new vector v2:

std::vector<Foo> v1, v2;

// populate v1 with at least 17 elements...

auto it = std::next(v1.begin(), 17);

std::move(v1.begin(), it, std::back_inserter(v2));  // ##

v1.erase(v1.begin(), it);

After line ##, the first 17 elements of v1 are still there, but they've been "moved-from", so they're in an indeterminate state.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • Won't just swap member of vector suffice here? – Ivaylo Strandjev Feb 21 '13 at 14:25
  • @IvayloStrandjev: depends on what you want to do... if you want to move a segment from the middle of the first vector, I don't see how swapping would be advantageous. – Kerrek SB Feb 21 '13 at 14:26
  • @IvayloStrandjev Swap with *what*? You're filling a hole; not exchanging gifts. Or I don't understand the concept (hell, at this wee hour without my caffeine its a miracle I understand Kerrek's post). Edit: OK. I see your post. *now* I understand *both* concepts. – WhozCraig Feb 21 '13 at 14:28
  • 3
    Another option (instead of `std::move`) is to use `std::make_move_iterator` and `vector.assign()` – Dave S Feb 21 '13 at 14:31
  • @WhozCraig yes, I missed the part that he only needed to exchange a subvector. Still I will keep my answer as it suggests an option not mentioned elsewhere. – Ivaylo Strandjev Feb 21 '13 at 14:37
  • I am getting courrpted doubly linked list error by using this. Also I used all the three parameters being passed to std::move separately and they all worked well. – PHcoDer Sep 21 '16 at 05:14
  • @PHcoDer: Are you sure you don't have overlapping ranges? – Kerrek SB Sep 21 '16 at 12:01
  • Doesn't `std::back_inserter()` use the `push_back()` function of the `v2`? In which case, will this not result in an unnecessary copy? – Adrian Dec 07 '17 at 00:27
  • @Adrian: Why would `push_back` result in copies? We're using move iterators, whose whole point is to move. – Kerrek SB Dec 07 '17 at 00:29
  • Nm, didn't realise that `push_back()` had a move operation. – Adrian Dec 07 '17 at 00:43
  • Wouldn't it be prudent to reserve the number of added values so that it doesn't have to reallocate all the time, or potentially over allocating, depending on allocation scheme? – Adrian Dec 07 '17 at 15:59
  • @Adrian: It sure would! – Kerrek SB Dec 07 '17 at 16:24
  • One last thing. You say that the elements in `v1` would be in an indeterminate state. However, they would have to be in a valid state, so it's not exactly indeterminate, is it? – Adrian Dec 07 '17 at 20:36
  • @Adrian: "Valid but indeterminate" is the way it's usually phrased. It means you cannot assume any particular value, hence you can only call member functions that have no preconditions. Typically you would of course reassign or destroy such objects. – Kerrek SB Dec 07 '17 at 20:38
  • One more last thing. Is there a way to move consecutive items of one vector into another, but not at the end? Or would I have to do a `std::rotate()` afterwards? – Adrian Dec 08 '17 at 23:26
44

The std::move lets you move the objects, as opposed to copying them, allowing for a potentially faster execution speed. The savings may be even greater when you move a range of values. However, when you do move a range from a container, the container still holds the places that were once occupied by these values.

You need to resize the container manually to remove these placeholders if you want to get rid of them (you don't have to, in case you would prefer reusing these container spots for other elements). One way to do it is to call vector::erase on the same range that you moved out of the container.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • 1
    good answer; but take care the initial vector is inan indeterminate state, since the move assignement constructor has been called, an the initial objects have been ripped the guts out to quote scott meyers – Gabriel Jun 22 '15 at 12:58
  • 1
    @Gabriel the vector isn't in an indeterminate state, but the moved-from objects are, unless specified specifically for that type of object (e.g. `unique_ptr`/`shared_ptr` both specify being `reset` after being moved-from). – rubenvb Jan 06 '20 at 14:43
5

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.

If you want to move the same amount of elements between two vectors, you can use swap_ranges

alestanis
  • 21,519
  • 4
  • 48
  • 67
  • 1
    @R.MartinhoFernandes Edited to be clearer. The OP was asking for one operation to do what he has to do in two operations, the second one always being erasing from the first vector. – alestanis Feb 21 '13 at 14:29