为什么std::move可以移动堆栈数据对象?

问题描述 投票:0回答:1

我对以下移动堆栈对象的代码感到困惑:

    int *pOuter = nullptr;
    {
        int j;
        std::cout << "top of the inner stack=" << &j << std::endl;
        int aData[] = {1,2,3,4,5};  // in a vanishing stack
        aData[0] = 0;  // to make sure that aData is on stack
        int *pInner = aData;
        std::cout << "Before Move: pOuter=" << pOuter << ", pInner=" << pInner << std::endl;
        pOuter = std::move(pInner);
        std::cout << "After Move: pOuter=" << pOuter << ", pInner=" << pInner << std::endl;
    }
    // aData[0] and pInner are no longer accessible
    int i;
    std::cout << "top of the outer stack=" << &i << std::endl;
    int aCover[] = {10,9,8,7,6,5,4,3,2,1};  // to reclaim stack
    std::cout << "aCover=" << aCover << ", pOuter=" << pOuter << ": ";
    for (i = 0; i < 5; ++i) {
        std::cout << *(pOuter + i) << ",";
    }
    std::cout << std::endl;

输出为:

top of the inner stack=0xb2125ff7cc
Before Move: pOuter=0, pInner=0xb2125ff7b0
After Move: pOuter=0xb2125ff7b0, pInner=0xb2125ff7b0
top of the outer stack=0xb2125ff804
aCover=0xb2125ff7d0, pOuter=0xb2125ff7b0: 0,2,3,4,5,

我很惊讶为什么它会起作用,因为堆栈对象 sData 应该被销毁并被 aCover 覆盖,这样 pOuter 应该会导致分段失败,或者至少指向 sCover。相反,aData 不会随内部堆栈一起销毁。

从显示的地址来看,我的猜测是C++编译器实际上在内部堆栈之前分配了以下堆栈对象:

int i; 
int aCover[] = {10,9,8,7,6,5,4,3,2,1};

即便如此,在我看来,pOuter 是一个悬空引用,它的工作是偶然的。

我说得对吗?

c++ move-semantics
1个回答
0
投票

正如评论中所指出的,

std::move
只是一种类型转换。那么它是如何工作的呢?

那么,我们以下面的代码为例:

Foo x;
Foo y = x;

这会在

x
中创建
y
的副本 - 即它调用
y
的复制构造函数来复制
x

而这个:

Foo x;
Foo y = std::move (x);

x
移至
y
,如果您不再关心
x
,则效率更高。

但是 - 这是关键 -

std::move
只有在
Foo
有合适的移动构造函数时才会做任何有用的事情,并且其签名是:

Foo &Foo (Foo &&other)

即参数

other
必须是
rvalue
,而
std::move
本质上是将
x
(以左值开始)转换为右值,从而告诉编译器调用移动构造函数而不是复制构造函数。

这就是std::move所做的

全部
。它本身不会移动任何东西。怎么可能呢?它不知道如何做,如果不存在合适的移动构造函数,编译器将回退到复制
x
。尤其是原始类型的情况。

© www.soinside.com 2019 - 2024. All rights reserved.