Moving elements from std::vector to another one
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.
Moving elements from one vector to another using erase-remove paradigm
I think that generally the point of these algorithms, is that you are doing what you want to achieve by applying functions. Once the functions have side-effects, it seems like if anything the net result is maybe misleading, and it might just be better to do a for loop.
That said, remember that C++ is not Java. A vector<Foo>
has to store Foo's, it just can't store references. However, there is still a problem with your whole idea.
myNewVec.push_back(x);
This line from your code will push a copy of x
into your new vector. Because it's a copy, you don't have to worry about sharing references. Now, for integers, copies and moves are the same. But for complex objects (like, say, a vector), a move can be way, way faster than a copy. And the only vector is anyway getting rid of x
, so we definitely want to move. So ideally we would change that line to:
myNewVec.push_back(std::move(x));
However, moving from an object clearly mutates it, and requires it not be const. The requirements for remove_if
however require that the function object passed is a predicate. This, in turn means that:
The function object pred shall not apply any non-constant function through the dereferenced iterator.
http://en.cppreference.com/w/cpp/concept/Predicate
In other words, your function must accept the result of dereferencing the iterator, but is not supposed to mutate it. Because it is not supposed to mutate it, you can never move from the original object, but have to make a copy of it. Therefore, I don't think there is any conformant, efficient implementation of this idea for non-trivial types.
Here is a reasonable implementation:
template <class T, class F>
void transfer_if_not(std::vector<T>& old, std::vector<T>& new, F pred)
{
auto part = std::partition(old.begin(), old.end(), pred);
std::move(part, old.end(), std::back_inserter(new));
old.erase(part);
}
This at least should not copy any elements. It will basically separate out the elements to stay and to leave in place in the original vector. Then efficiently move out the ones that are leaving. And then simply resize the array. As pointed out in the comments, this probably involves extra operations compared to what's optimal, but my sense is that the optimal version is not trivial to code up, and may involve tradeoffs (like more state), so it may not be a pure win.
Note that my algorithm accepts a vector specifically instead of iterators, because for other containers (say a linked list), this implementation is quite far from optimal.
Can I move the contents of one vector to the end of another?
You can use std::make_move_iterator
, so that accesses to the iterator returns rvalue references instead of lvalue references:
a.insert(a.end(), std::make_move_iterator(b.begin()), std::make_move_iterator(b.end()));
How do I move-append one std::vector to another?
insert
will work fine with std::move_iterator and std::make_move_iterator helper fucntion:
to.insert(to.end(),std::make_move_iterator(from.begin()),
std::make_move_iterator(from.end()));
When appending one vector to another, why is moving the elements any cheaper than copying them?
Indeed, if copying and moving an element costs the same (is in your example with elements of type int
), then there is no difference.
Moving only makes a difference for elements which themselves store their data on the heap, i.e. use allocated memory (for example if the elements are std::string
or std::vector<something>
). In this case, moving or copying the elements makes a (potentially huge) difference (provided the move constructor and operator=(value_type&&)
are properly implemented/enabled), since a move merely copies the pointer to the allocated memory, while a copy is deep: it allocates new memory and copies all data, including recursive deep copies if applicable.
As to the costs associated with the data stored in std::vector
, there are some costs if the appended elements exceed capacity. In this case, the whole vector will be resized, including moving all its elements. The reason for this is that std::vector
, by specification, stores all its elements in a single array. If appending containers is a frequent operation in your code, you may want to consider other containers, such as std::list
or std::deque
.
C++ Moving elements from one vector to another
The std::vector<...>
does have an insert()
method. Actually, it has multiple overloaded insert()
methods but all of them take an iterator indicating the position where to insert the passed value(s). If it is OK to append the value to the end, you can use push_back()
:
peerList.push_back(p);
How to move the first element of a vector to the end of another
Erasing while iterating is always messy. If you erase the current element, how can ++
work?
Instead try
while (!ToRun.empty()) // loop until empty
{
ToRun.front()->dump(os);
Completed.push_back(std::move(ToRun.front()));
ToRun.erase(ToRun.begin());
}
or something similar
But that's gross when you stop and think about it. Every erase
shifts the rest of the elements in ToRun
back one slot adding a large amount of unnecessary iterating and copying.
for (auto & run: ToRun) // or old school iterator if you prefer
{
run->dump(os);
Completed.push_back(std::move(ToRun.front()));
}
toRun.clear();
clear
executes only once and obliterates the whole container in one shot. Much cleaner. Just one iteration to make sure the destructors are called, and you don't destroy pointers. Winning!
will have the same effect (assuming this isn't multithreaded, and if it is you have serious concurrency issues).
Cheersandhth.-Alf also brings up the point that since ToRun
appears to contain pointers, std::move
is not necessary. All that is being moved is a pointer, and that's trivial effort.
STL vector: Moving all elements of a vector
Using C++11, it's as simple as:
A = std::move(B);
Now A
contains the elements that were previously held by B
, and B
is now empty. This avoids copying: the internal representation is simply moved from B
to A
, so this is an O(1)
solution.
As for C++03, as Prætorian states, you could swap the vectors. There is a specialization of the std::swap
function, which takes std::vector
s as its arguments. This effectively swaps the internal representation, so you end up avoiding creating copies of the elements held by them. This function works in O(1)
complexity as well.
Moving the contents of one vector to another
Here’s a complete workflow of how to handle spawning and despawning enemies. Note that there are no pointers at all involved.
Spawning an enemy:
if (if random_float_between_0_and_1() < 0.002)
enemies.push_back(Enemy{arguments});Despawning enemies; according to your comment below, should look something like this:
auto last_iter = std::remove_if(enemies.begin(), enemies.end(), is_dead);
enemies.erase(last_iter, enemies.end());Here,
is_dead
is a function that takes anEnemy const&
and determines whether it collided with a player or the screen bounds:bool is_dead(Enemy const& enemy) {
return outside_screen_area(enemy) or near_player(enemy);
}The functions
outside_screen_area
andnear_player
should be straightforward for you to implement.To understand how the code above works, consult the documentations of
std::remove
andstd::vector::erase
.
Another thing: implement the function random_float_between_0_and_1
in terms of the standard library random library that ships with C++11. Don’t use std::rand
or modulo operations on integer random numbers, they work badly (i.e. they’re not truly uniformly distributed and will give skewed results).
Related Topics
How to Correctly and Standardly Compare Floats
Why Catch an Exception as Reference-To-Const
Why Can't Static_Cast Be Used to Down-Cast When Virtual Inheritance Is Involved
Difference in Performance Between Msvc and Gcc for Highly Optimized Matrix Multplication Code
Why Can't a Derived Class Call Protected Member Function in This Code
Why Is Allocator::Rebind Necessary When We Have Template Template Parameters
Know If .Lib Is Static or Import
How to Read-Write Into/From Text File with Comma Separated Values
C++11 Initializer List Fails - But Only on Lists of Length 2
Calling a Function for Each Variadic Template Argument and an Array
C++: Argument Passing "Passed by Reference"
Variable Assignment in an "If" Condition
How to Use Createfile, But Force the Handle into a Std::Ofstream
Using Class Name in a Class Template Without Template Parameters