I was expecting that std::make_move_iterator
will always move contents, but it seems not.
It looks like it is moving elements in vector<string>
but not in vector<int>
.
See the below code snippet:
#include <iostream>
#include <iterator>
#include <string>
#include <vector>
void moveIntVector()
{
std::cout << __func__ << std::endl;
std::vector<int> v1;
for (unsigned i = 0; i < 10; ++i) {
v1.push_back(i);
}
std::vector<int> v2(
std::make_move_iterator(v1.begin() + 5),
std::make_move_iterator(v1.end()));
std::cout << "v1 is: ";
for (auto i : v1) {
std::cout << i << " ";
}
std::cout << std::endl;
std::cout << "v2 is: ";
for (auto i : v2) {
std::cout << i << " ";
}
std::cout << std::endl;
}
void moveStringVector()
{
std::cout << __func__ << std::endl;
std::vector<std::string> v1;
for (unsigned i = 0; i < 10; ++i) {
v1.push_back(std::to_string(i));
}
std::vector<std::string> v2(
std::make_move_iterator(v1.begin() + 5),
std::make_move_iterator(v1.end()));
std::cout << "v1 is: ";
for (auto i : v1) {
std::cout << i << " ";
}
std::cout << std::endl;
std::cout << "v2 is: ";
for (auto i : v2) {
std::cout << i << " ";
}
std::cout << std::endl;
}
int main()
{
moveIntVector();
moveStringVector();
return 0;
}
The result is:
moveIntVector
v1 is: 0 1 2 3 4 5 6 7 8 9 # I expect this should be `0 1 2 3 4` as well!
v2 is: 5 6 7 8 9
moveStringVector
v1 is: 0 1 2 3 4
v2 is: 5 6 7 8 9
I'm on Ubuntu 14.04, gcc 4.8.2
and the code is compiled with -std=c++11
Could you explain why std::make_move_iterator
have different behaviour on vector<int>
and vector<string>
? (Or is it a bug?)
The behaviour is expected. A move from both vectors leaves the original v1
with 5 moved-from elements in their second half.
The difference is that when the strings are moved, what is left behind is empty strings. This is because it is a very efficient way to move strings, and leave the moved-from string in a self-consistent state (Technically, they could be left to hold the value "Hello, World, nice move!"
, but that would incur extra cost). The bottom line is that you don't see those moved-from strings in your output.
In the case of the int
vectors, there is no way to move an int
that is more efficient than copying it, so they are just copied over.
If you check the sizes of the vectors, you will see the v1
have size 10 in both cases.
Here's a simplified example to illustrate that the moved from strings are left empty:
#include <iostream>
#include <iterator>
#include <string>
#include <vector>
int main()
{
std::vector<std::string> v1{"a", "b", "c", "d", "e"};
std::vector<std::string> v2(std::make_move_iterator(v1.begin()),
std::make_move_iterator(v1.end()));
std::cout << "v1 size " << v1.size() << '\n';
std::cout << "v1: ";
for (const auto& s : v1) std::cout << s << " - ";
std::cout << '\n';
std::cout << "v2 size " << v2.size() << '\n';
std::cout << "v2: ";
for (const auto& s : v2) std::cout << s << " - ";
std::cout << '\n';
}
Output:
v1 size 5
v1: - - - - -
v2 size 5
v2: a - b - c - d - e -
When we talk about a move we are not talking about moving the object itself (it remains intact). What gets moved are its internal data. This may or may not affect the value of the object whose internal data gets moved.
That is why your int
array doesn't loose its original int
s. As to your string example, it still has the original std::strings
just like the int
example but their internal values have changed to empty strings.
It is important to remember that internally a std::string
(essentially) holds a pointer to a character array. So when you copy a std::string
you copy every element of the character array. A move, however, avoids doing all that copying by copying the internal pointer instead.
But if the move operation stopped there that would leave both std::string
s pointing at the same character array and changing the character data pointed to by either std::string
would also change the other's. So when you move a string it is not enough to merely copy the internal pointer, you have to make the internal pointer of the std::string
you moved from point to a new blank character array so that it can no longer affect the string its data was moved to.
When moving an int
there is no further action required after the copy of its data. There are no pointers involved so after the copy both ints contain independent data.