Why does move not erase the source?

Hi,

I have this code:

1
2
3
4
5
6
7
8
vector<string> coll1 = { "Hello", "this", "is", "an", "example" };
list<string> coll2;

move(coll1.begin(), coll1.end(), back_inserter(coll2));
coll1.assign({ "Hello", "this", "is", "an", "example" });

move(coll2.begin(), coll2.end(), ostream_iterator<string>(cout, " "));
cout << endl;


The second move does not erase the strings of coll2 while the first one does!

move should empty the strings of coll2!!


Why then this behavior?

Regards
Move is specifically for situations where you don't care what happens to the source. If you want to emptied, copy it normally and empty it yourself.
No, you misunderstand my question. If you have a string and move from it, the source string becomes empty...

but when you have a collection of strings and you move() the entire collection, then on some occasions, its string elements aren't empty!! Why?



Last edited on
Take a look at this:

https://cplusplus.com/reference/algorithm/move/

Especially:
After the call, the elements in the range [first,last) are left in an unspecified but valid state.


I.e. to make sure the source is empty you need to clear it.
A possible implementation of std::move does use: *d_first = std::move(*first);

1
2
3
4
5
6
7
8
9
10
vector<string> coll1;
list<string> coll2;

auto first = coll1.begin();
auto d_first = back_inserter(coll2);
*d_first = std::move(*first);  // #1

auto second = coll2.begin();
auto x_first = ostream_iterator<string>(cout, " ");
*x_first = std::move(*second); // #2 

On #1 you move from vector::iterator to back_inserter.
On #2 you move from list::iterator to ostream_iterator.


Your question is essentially: How back_inserter and ostream_iterator handle rvalue references?

The short answer is: differently.
move(...) uses move(T) which returns an xvalue reference type. How this xvalue ref type is processed by the caller (eg on the right side of an assignment with operator=() overload) is down to the caller. The caller can even ignore an xvalue ref and treat as a value. All you can rely upon is that after move(), the value used in move() is left in an unspecified but valid state. The emphasis being on unspecified.
std::move is used to indicate that an object may be "moved from". It does nothing else!

Move constructors and move assignment operators can make use of this as a sort of shortcut that allows them to "steal" resources from the original object, instead of having to actually duplicate those resources. Therefore, after an object was actually "moved from", that object is left in an "unspecified" (but valid) state. But, by no means, you have any guarantee that the object which was "moved from" has been "erased". The move constructors or move assignment operator simply leaves the original object in whatever state it deems convenient...

(In other words, the move constructors or move assignment operator will not do any extra work to "clean up" the original object; they just "take" whatever resources they want to re-use and then they do the minimal amount of work that is required to ensure that the original object will be in some "valid" state – since leaving the object in a "broken" state is not allowed – but do not expect more)
Last edited on
JUANDENT wrote:
If you have a string and move from it, the source string becomes empty...

The standard doesn't guarantee that.

https://en.cppreference.com/w/cpp/string/basic_string/basic_string
8) Move constructor. Constructs the string with the contents of other using move semantics. other is left in valid, but unspecified state.
Last edited on
thank you all!!
Topic archived. No new replies allowed.