std::move 是让堆栈碎片化,还是在将对象移动到调用方后重新排列堆栈?

Does std::move leave stack fragmented or is the stack rearranged after moving an object to the caller?

提问人:muaz 提问时间:6/29/2022 更新时间:7/5/2022 访问量:203

问:

我已经阅读了几个答案和许多关于移动语义的文章,所以它只是一个静态转换到右值引用,这个问题是关于它对堆栈的影响,那么堆栈在移动后是否留下了碎片?还是以某种方式重新排列?由于堆栈移动的对象不受堆栈展开的影响。以下代码片段运行良好:

#include <memory>
#include <cassert>

struct A {
    int x = 0;
};

struct B {
    A a;
};

void SetB(B& b) {
    A a1{6}; //pushing into the stack.
    A a2{7}; //pushing into the stack.
    b.a = std::move(a2);
}

int main()
{
    B b;
    SetB(b);
    assert( b.a.x == 7);
}

a2在堆栈帧中定义,但在完成并返回 contorl 后仍然可用。然而; 是不是更接近堆叠框架?SetBSetBmaina1main

C++ C++11 移动 堆栈展开

评论

8赞 user17732522 6/29/2022
"由于堆栈移动的对象不受堆栈展开的影响“:这个前提是错误的。移出对象仍然是普通对象,只是处于移出状态。当它们的作用域离开时,它们仍然会像往常一样调用析构函数,而不是更早。
0赞 user17732522 6/29/2022
我不知道你是怎么得到最后两句话的。当返回两者并被销毁时。局部变量的生存时间永远不会超过它们抑制的范围。SetBa1a2
0赞 user17732522 6/29/2022
此外,移动一个琐碎的类(例如)与复制它完全相同。您需要提供一个专门的移动构造函数或赋值运算符,该运算符利用权限以某种方式移动,例如通过重用动态分配。否则没有区别。 只是从左值中创建一个 x值,以便右值引用可以绑定到它,而右值引用通常只是一个约定,即“我不再需要引用对象的状态,只要你让它处于可破坏状态,就可以随心所欲地使用它”。Astd::move
1赞 JaMiT 6/29/2022
@muaz “如果移动的对象没有真正移动”,那么到目前为止,一切都很好 -- “在该帧展开后,它的值仍然在那里” -- 你已经在这里做了一个关键的语义跳跃,从对象。移动的对象并没有真正移动,但它的值可能会被移动(或复制,这实际上是移动的一种特殊情况,尽管这看起来有悖常理)。你看过吗 A:什么是移动语义?
1赞 JaMiT 6/29/2022
嗯。。。我曾经写过一些类似的东西。答:隐式移动与复制操作和包含是否有助于您理解?

答:

6赞 lorro 6/29/2022 #1

C++标准中没有“堆栈”,它是一个实现细节。因此,“通用”答案是困难的,因为实现可能会对给定架构的堆栈产生任何影响

也就是说,它不会从堆栈中删除参数,它仍然处于有效但未定义的状态。对于您自己的类,您甚至可以实现它。因此,不可能从任何地方“删除”变量 - 当然,除非在编译器的优化阶段,如果它不再被访问。std::move()

a2完成后可用。该值被移动到 因此,应包含之前包含的,但它不是相同的变量(也不是相同的内存地址)。SetBb.ab.aa2

评论

0赞 muaz 6/29/2022
预期如何包含值,该值是否从堆栈帧复制到一个中?所以 std::move 不是那么便宜的操作吗?b.aa2SetBmain
0赞 Goswin von Brederlow 6/29/2022
std::move实际上什么也没做。它只会影响编译器在编译过程中看到的类型。移动分配可以做一些工作。
0赞 lorro 6/30/2022
@muaz 纯粹是推测性的,你可能来自语言背景(可能是 PHP、Java),或者只是一种思维方式,对变量的赋值工作方式不同。在某些语言中,写作会创建一个“数字对象”,变量只引用它。c++ 并非如此 - 在 c++ 中,您的变量具有地址,并且变量的值(存储在给定地址上)会更新,而不是变量的“目标”会更新。该原理图存在,但它是指针(在这种情况下会不必要地变慢)。7
0赞 muaz 6/30/2022 #2

有了这个答案,我想总结一下我得到的所有信息,这些信息确实回答了我的问题:

  • 对于堆栈中分配的所有变量,默认执行正常复制(如果这些变量很大,则非常昂贵),这是我在 JaMit、user17732522 和 lorro 回答后测试的。最初这个问题是因为虽然编译器会添加一些措施来管理堆栈而不复制所有内容,但看起来我高估了(没有堆栈碎片或重新排列,只有复制)。所以在上面的例子中,if的定义如下:movemoveA
struct A {
    int x[1000] = {};
};

移动会导致按原样复制整个数组。A

  • 移动堆资源完全取决于程序员,程序员将转换为右值引用,一旦在 CTOR 中分配或调用,它将调用移动赋值或移动构造函数,如果这些是默认的,它会对指针进行浅拷贝,这可能会导致由于悬空指针而导致分段错误。move