Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a standard way of moving a range into a vector?

Consider the following program which inserts a range of elements into a vector:

vector<string> v1; vector<string> v2;  v1.push_back("one"); v1.push_back("two"); v1.push_back("three");  v2.push_back("four"); v2.push_back("five"); v2.push_back("six");  v1.insert(v1.end(), v2.begin(), v2.end()); 

This efficiently copies the range, allocating enough space in the target vector for the entire range so that a maximum of one resize will be required. Now consider the following program which attempts to move a range into a vector:

vector<string> v1; vector<string> v2;  v1.push_back("one"); v1.push_back("two"); v1.push_back("three");  v2.push_back("four"); v2.push_back("five"); v2.push_back("six");  for_each ( v2.begin(), v2.end(), [&v1]( string & s ) {     v1.emplace_back(std::move(s)); }); 

This performs a successful move but doesn't enjoy the benefits that insert() has with regard to preallocating space in the target vector, so the vector could be resized several times during the operation.

So my question is, is there an insert equivalent which can move a range into a vector?

like image 456
Benj Avatar asked May 23 '12 12:05

Benj


People also ask

How do I move a value to a vector in C++?

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.

Does vector have a move constructor?

No. It doesn't call the move constructor. To call move constructor of element you will have to call std::move while pushing to vector itself.


1 Answers

You use a move_iterator with insert:

v1.insert(v1.end(), make_move_iterator(v2.begin()), make_move_iterator(v2.end())); 

The example in 24.5.3 is almost exactly this.

You'll get the optimization you want if (a) vector::insert uses iterator-tag dispatch to detect the random-access iterator and precalculate the size (which you've assumed it does in your example that copies), and (b) move_iterator preserves the iterator category of the iterator it wraps (which is required by the standard).

On an obscure point: I'm pretty sure that vector::insert can emplace from the source (which is irrelevant here, since the source is the same type as the destination, so an emplace is the same as a copy/move, but would be relevant to otherwise-identical examples). I haven't yet found a statement that it's required to do so, I've just inferred it from the fact that the requirement on the iterator pair i,j passed to insert is that T be EmplaceConstructible from *i.

like image 69
Steve Jessop Avatar answered Sep 18 '22 14:09

Steve Jessop