0

我正在使用 VC++2012 运行以下代码:

#include <utility>

struct A
{
    int* m_p;

    A() { m_p = new int; }
    ~A() { delete m_p;  }

    A(const A& otherA)
    {
        m_p = new int;

        // BOOM!
        *m_p = *otherA.m_p;
    }
};

A&& CreateA()
{
    A a;
    return std::move(a);
}

int _tmain(int argc, _TCHAR* argv[])
{
    A a2 = CreateA();
    return 0;
}

在创建 a2 期间,A 的复制 ctor 被调用 - 并且崩溃,因为在 CreateA() 中创建的源对象已经被销毁。这是标准行为吗?这可能是编译器错误吗?

请注意,如果您将 a2 的类型从“A”更改为“const A&”,则不会发生崩溃——这加强了对它确实是一个错误的怀疑。任何人都可以对此有所了解吗?

注意:我完全知道这不是 rvalue-refs 的预期用途,这个例子是人为的。只是希望能更好地掌握这种新类型的行为。

4

2 回答 2

2

您不能访问其范围之外的局部变量。右值引用不会改变这一点:它们仍然是引用。呈现的代码具有未定义的行为,因为它返回对局部变量的引用,然后访问它。

不要返回右值引用。绝大多数时间都是愚蠢的。而是返回值:

A CreateA()
{
    A a;
    return a; // a move here is automatic
              // unless you are using a compiler with outdated rules like MSVC
    //return std::move(a); // ok, poor MSVC
    // alternatively:
    //return A{}; //or 
    //return A();
}

当你A const& a2 = CreateA();什么都不写时会崩溃,因为你实际上并没有访问任何对象。你所要做的就是抓住一个悬空的参考。然而,这段代码甚至不是格式正确的,它只是碰巧编译,因为 MSVC 有一些过时的引用绑定规则。

所以,基本上,这些行为是编译器错误和未定义行为的混合 :)

于 2012-12-21T16:18:53.663 回答
2

看看你的代码发生了什么:

  1. CreateA()叫做
  2. 在函数内部,创建了一个局部变量类型A
  3. 您创建一个指向此局部变量的右值引用
  4. 你返回这个右值引用
  5. 当您返回时,A右值引用指向的 type 对象超出范围并被销毁
  6. 引用现在指向一个被破坏的对象
  7. 您尝试初始化为曾经存在于函数调用a2中的对象的副本

而且……这行不通。你试图复制的对象已经死了并且消失了。未定义的行为。

不要那样做。:)

在 C++ 中,引用不会影响被引用对象的生命周期。没有“我指着这个物体,所以你不能破坏它!”。

永远不要返回对本地对象的引用。它不起作用,所以......只是不要这样做。

于 2012-12-21T16:31:53.020 回答