Can std :: move local stack variables?

Pay attention to the following code:

struct MyStruct { int iInteger; string strString; }; void MyFunc(vector<MyStruct>& vecStructs) { MyStruct NewStruct = { 8, "Hello" }; vecStructs.push_back(std::move(NewStruct)); } int main() { vector<MyStruct> vecStructs; MyFunc(vecStructs); } 

Why does this work?

When calling MyFunc, the return address must be pushed onto the stack of the current thread. Now create a NewStruct object to be created, which must also be pushed onto the stack. With std :: move, I tell the compiler that I no longer plan on using the NewStruct link. He can steal memory. (The push_back function is a function with move semantics.)

But when the function returns, and NewStruct goes out of scope. Even if the compiler does not delete the memory occupied by the originally existing structure from the stack, it should at least delete the previously saved return address.

This will result in a fragmented stack, and future allocations will overwrite the "moved" memory.

Can someone explain this to me please?


EDIT: First of all: Thank you very much for your answers. But from what I learned, I still canโ€™t understand why the following does not work, how I expect it to work:

 struct MyStruct { int iInteger; string strString; string strString2; }; void MyFunc(vector<MyStruct>& vecStructs) { MyStruct oNewStruct = { 8, "Hello", "Definetly more than 16 characters" }; vecStructs.push_back(std::move(oNewStruct)); // At this point, oNewStruct.String2 should be "", because its memory was stolen. // But only when I explicitly create a move-constructor in the form which was // stated by Yakk, it is really that case. } void main() { vector<MyStruct> vecStructs; MyFunc(vecStructs); } 
+7
c ++ c ++ 11 move-semantics move
source share
2 answers

First, std::move does not move, and std::forward not forwarded.

std::move is a link to the rvalue link. By convention, rvalue links are considered to be โ€œlinks to which you are allowed to transfer data, since the caller promises they no longer need this data.โ€

On the other hand of the fence, rvalue links are implicitly bound to the return value of std::move (and sometimes are forwarded), to temporary objects, in some cases when returning local from a function and when using a member, a temporary or moved object.

What happens inside a function using an rvalue reference is not magic. It cannot require storage directly inside the object in question. However, he can tear out his guts; he has permission (by convention) to mess with his internal state of the arguments if he can make the operation faster this way.

Now C ++ will automatically write some move constructors for you.

 struct MyStruct { int iInteger; string strString; }; 

In this case, he will write something that looks something like this:

 MyStruct::MyStruct( MyStruct&& other ) noexcept(true) : iInteger( std::move(other.iInteger) ), strString( std::move(other.strString) ) {} 

That is, it will create an elemental displacement construct.

When you move an integer, nothing interesting happens. There is no benefit in being involved in the original integer state.

When you move std::string , we get some efficiency. The C ++ standard describes what happens when you go from one std::string to another. Basically, if the source std::string uses a heap, the heap storage is transferred to the destination std::string .

This is a common C ++ container template; when you move from them, they steal the "storage heaped" source container and reuse it at the destination.

Note that the source of std::string remains std::string , only the one that has "dropped guts". Most containers like things have remained empty, I donโ€™t remember if std::string makes this guarantee (maybe not because of SBO), and now it doesnโ€™t matter.

In short, when you switch from something, its memory is not "reused", but the memory that it has can be reused.

In your case, MyStruct has std::string , which can use the allocated memory heap. This memory allocated by the heap can be transferred to MyStruct stored in std::vector .

Going a little down the rabbit hole, "Hello" is likely to be so short that SBO will happen (slight buffer optimization), and std::string does not use heaps at all. In this particular case, there can be no performance improvement due to move ing.

+9
source share

Your example can be reduced to:

 vector<string> vec; string str; // populate with a really long string vec.push_back(std::move(str)); 

This still raises the question: "Is it possible to move local stack variables." It just removes some extraneous code to make it easier to understand.

The answer is yes. . Code like the one above can benefit from std::move , because std::string - at least if the content is large enough - keeps its actual data on the heap, even if the variable is on the stack.

If you are not using std::move() , you can expect code like the one above to copy the contents of str , which can be arbitrarily large. If you use std::move() , only the direct members of the line will be copied (the movement does not need to โ€œnullifyโ€ the old locations), and the data will be used without modification or copying.

Basically the difference between this:

 char* str; // populate with a really long string char* other = new char[strlen(str)+1]; strcpy(other, str); 

against

 char* str; // populate with a really long string char* other = str; 

In both cases, the variables are on the stack. But there is no data.

If you have a case where indeed all the data is on the stack, for example std::string with "optimizing small strings" or a string containing integers, then std::move() will not buy anything.

+5
source share

All Articles