I think your misunderstanding lies in how your data is actually stored. The struct object will be on the stack, containing an int
and a std::string
object, yes. However, the actual string contents are stored on the free store, not on the stack (ignoring an optimization that isn't important here).
Now let's remove std::move
and move semantics from the picture. What would happen? The vector will create a new element on the end that is copy-constructed from your stack variable. This means that the new object will have a copy of the int
value and a copy of the std::string
value. Copying the std::string
entails allocating another block of memory on the free store and copying the data into there. In addition, any members such as size are copied as your int
is. When your stack variable goes out of scope, its int and the string will be destroyed, which causes the string to clean itself up, not touching the new copy in any way. What you're left with after that is the new object at the end of the vector with its own copy of the data.
If you'll excuse the poor diagram:

How does moving change this? Moving is simply an optimization to avoid unnecessary copies when possible. Copying is a valid move tactic, just not a good one if you can do better. Using std::move
will ultimately cause the vector to move your stack object when creating the new object at the end of the vector. The int will still be copied because there's no optimization to be had there. However, the string can take advantage of the fact that this free store data is no longer needed. The new object can simply steal the pointer, copy the size etc., and tell the moved-from object not to clean up that free store data (transferring ownership).
If you'll excuse a slightly modified version of the original poor diagram:

We saved allocating a separate block for the string, but that's all. The other data was all still copied. The actual string data will be cleaned up by the vector element when it is removed or the vector is destroyed. The stack element now has nothing to clean up because moving has "stolen" the string data rather than copying it. You can think of it as nulling out the stack object's pointer, even though an implementation is free to represent an empty string differently.
What I said doesn't fully apply here because major implementations of std::string
are clever enough to avoid extra allocations for small strings. That simply means the string data would need to be copied because as you say, it would die when the original object does. Any extra allocations are open to move optimizations, though.
As for your second example, there's no general optimization you can do to move a raw pointer; it's (usually) just 4 or 8 bytes to copy over. Moving that into the vector would copy the pointer value, leading to a dangling pointer within the vector once the function ends. The myStr
pointer within the function would be destroyed when the function ends and not affect the vector in any way.