是否可以std :: move本地堆栈变量? [英] Is it possible to std::move local stack variables?

查看:346
本文介绍了是否可以std :: move本地堆栈变量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请考虑以下代码:

  struct MyStruct 
{
int iInteger;
字符串strString;
};

void MyFunc(vector< MyStruct>& vecStructs)
{
MyStruct NewStruct = {8, Hello};
vecStructs.push_back(std :: move(NewStruct));
}

int main()
{
vector< MyStruct> vecStructs;
MyFunc(vecStructs);
}

为什么这样做?



在调用MyFunc的那一刻,返回地址应放在当前线程的堆栈上。现在创建创建的NewStruct对象,该对象也应放在堆栈上。通过std :: move,我告诉编译器,我不再打算使用NewStruct引用了。他可以偷走记忆。 (push_back函数是具有移动语义的函数。)



但是当函数返回并且NewStruct超出范围时。即使编译器不会从堆栈中删除原来已存在的结构所占用的内存,他至少也必须删除以前存储的返回地址。



到零散的堆栈,将来的分配将覆盖移动的内存。



有人可以向我解释一下吗?






编辑:
首先:非常感谢您的回答。
但是,据我了解,我仍然无法理解,为什么以下各项无法正常工作:

  struct MyStruct 
{
int iInteger;
字符串strString;
字符串strString2;
};

void MyFunc(vector< MyStruct>& vecStructs)
{
MyStruct oNewStruct = {8, Hello,肯定超过16个字符};
vecStructs.push_back(std :: move(oNewStruct));

//此时,oNewStruct.String2应该为,因为其内存被盗。
//但是只有当我以Yakk所说的
//形式显式创建一个移动构造函数时,确实是这种情况。
}

void main()
{
vector< MyStruct> vecStructs;
MyFunc(vecStructs);
}


解决方案

首先, std :: move 不移动, std :: forward 不转发。



std :: move 是对右值引用的强制转换。按照惯例,右值引用被视为允许您将数据移出引用,因为调用方保证不再需要这些数据了。



在篱笆的另一侧,右值引用隐式绑定到 std :: move (有时是转发)的返回值,临时对象,在某些情况下返回a



使用右值引用的函数内部发生的事情不是魔术。它不能直接在相关对象内声明存储 。但是,它可以撕破它的胆量。如果它可以更快地执行操作,它有权(按照惯例)弄乱其参数的内部状态。



现在,C ++会自动为您编写一些move构造函数。

  struct MyStruct 
{
int iInteger;
字符串strString;
};

在这种情况下,它将编写大致如下所示的内容:

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





当您移动整数时,不会发生任何有趣的事情。弄乱源整数的状态没有任何好处。



当移动 std :: string 时,我们获得了一些效率。 C ++标准描述了当您从一个 std :: string 移到另一个时会发生什么。基本上,如果源 std :: string 正在使用堆,则堆存储将转移到目标 std :: string



这是C ++容器的一般模式;当您从它们移出时,它们会窃取源容器的堆分配存储,并在目标位置重用它。



请注意,源 std :: string 仍然是 std :: string ,只是其中一个胆量被撕裂的东西。大多数类似容器的东西都是空的,我不记得 std :: string 是否做出了保证(可能不是由于SBO造成的),这并不重要吧。现在。



简而言之,当您从某处移开时,它的内存不会重用,但它拥有的内存可以被重用。 / p>

在您的情况下, MyStruct 具有 std :: string 可以使用堆分配的内存。可以将堆分配的内存移到存储在 std :: vector 中的 MyStruct 中。



再往下钻, Hello 可能太短,以至于发生SBO(小缓冲区优化),并且 std :: string 根本不使用堆。对于这种特殊情况,由于 move ing
,性能可能几乎没有改善。

Please consider 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?

At the moment when MyFunc is called, the return address should be placed on the stack of the current thread. Now create the NewStruct object gets created, which should be placed on the stack as well. With std::move, I tell the compiler, that i do not plan to use the NewStruct reference anymore. He can steal the memory. (The push_back function is the one with the move semantics.)

But when the function returns and NewStruct falls out of scope. Even if the compiler would not remove the memory, occupied by the originally existing structure from the stack, he has at least to remove the previously stored return address.

This would lead to a fragmented stack and future allocations would 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 have learned, I still cannot understand, why the following does not work like 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);
}

解决方案

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

std::move is a cast to an rvalue reference. By convention, rvalue references are treated as "references you are permitted to move the data out of, as the caller promises they really don't need that data anymore".

On the other side of the fence, rvalue references implicitly bind to the return value of std::move (and sometimes forward), to temporary objects, in certain cases when returning a local from a function, and when using a member of a temporary or a moved-from object.

What happens within the function taking an rvalue reference is not magic. It cannot claim the storage directly within the object in question. It can, however, tear out its guts; it has permission (by convention) to mess with its arguments internal state if it can do the operation faster that way.

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

struct MyStruct
{
  int iInteger;
  string strString;
};

In this case, it will write something that roughly looks like this:

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

Ie, it will do an element-wise move construct.

When you move an integer, nothing interesting happens. There isn't any benefit to messing with the source integer's state.

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

This is a general pattern of C++ containers; when you move from them, they steal the "heap allocated" storage of the source container and reuse it in the destination.

Note that the source std::string remains a std::string, just one that has its "guts torn out". Most container like things are left empty, I don't recall if std::string makes that guarantee (it might not due to SBO), and it isn't important right now.

In short, when you move from something, its memory is not "reused", but memory it owns can be reused.

In your case, MyStruct has a std::string which can use heap allocated memory. This heap allocated memory can be moved into the MyStruct stored in the std::vector.

Going a bit further down the rabbit hole, "Hello" is likely to be so short that SBO occurs (small buffer optimization), and the std::string doesn't use the heap at all. For this particular case, there may be next to no performance improvement due to moveing.

这篇关于是否可以std :: move本地堆栈变量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆