与move constuctor配对时发生意外的析构函数调用

Phi*_*gan 1 c++ visual-c++ move-semantics c++11 visual-studio-2012

以下代码是在Visual Studio 2012 Express for Windows Desktop中编译和运行的,作为学习练习.

#include <cstdio>

class X
{
public:
    X()  { printf("default constructed\n"); }
    ~X() { printf("destructed\n");}
    X(const X&) { printf("copy constructed\n"); }
    X(X&&) { printf("move constructed\n"); }
    X & operator= (const X &) { printf("copy assignment operator\n"); }
};

X A() {
    X x;
    return x;
}

int main() {
    {
        A();
    }
    std::getchar();
}
Run Code Online (Sandbox Code Playgroud)

在禁用编译器优化(/ Od)的情况下编译时,结果输出表明析构函数被调用两次.考虑到只构造了一个对象,这是一个问题.为什么析构函数被调用两次?如果班级管理自己的资源,这不是一个问题吗?

default constructed
move constructed
destructed
destructed   <<< Unexpected call 
Run Code Online (Sandbox Code Playgroud)

我尝试了几个实验来尝试解释输出,但最终这些并没有导致任何有用的解释.

实验1:在使用优化(/ O1或/ O2)编译相同代码时,结果输出为:

default constructed
destructed
Run Code Online (Sandbox Code Playgroud)

这表明命名返回值优化已经删除了对移动构造函数的调用,并掩盖了底层问题.

实验2:禁用优化并注释掉移动构造函数.产生的输出是我的预期.

default constructed
copy constructed
destructed
destructed
Run Code Online (Sandbox Code Playgroud)

Mic*_*urr 7

请记住,当一个对象是移动操作的源时,它仍然会被销毁.因此,移动的来源需要将自己置于一种状态,即被破坏的状态不会释放它不再拥有的资源(因为它们被移动到另一个对象).例如,源对象中的任何原始指针(现在将由移动构造对象拥有)应设置为NULL.