移动构造函数似乎没有执行

Sta*_*ked 12 c++ c++11

这是我第一次使用C++ 0x rvalue引用进行实验,似乎有些奇怪的事情正在进行中.

在下面的代码示例中,factory函数MakeWindow按值返回Window对象.调用者使用它来初始化Window对象.如果我理解正确,这应该调用移动构造函数.为了检测到这一点,我在那里抛出异常.最重要的是我禁用了复制构造函数:

#include <iostream>


// Fake WinAPI
typedef void* HWND;
HWND CreateWindow() { return (void*)1; }
void DestroyWindow(HWND) { }
// End WinAPI


// C++ WinAPI Wrapper Library
class Window
{
public:
    Window(HWND inHandle) :
        mHandle(inHandle)
    {
        std::cout << "Window constructor. Handle: " << inHandle << std::endl;
    }

    Window(Window && rhs) :
        mHandle(rhs.mHandle)
    {
        std::cout << "Window move constructor. Handle: " << mHandle << std::endl;
        rhs.mHandle = 0;
        throw 1; // this is my "breakpoint"
    }

    ~Window()
    {
        std::cout << "Window destructor. Handle: " << mHandle << std::endl;
        if (mHandle)
        {
            DestroyWindow(mHandle);
        }
    }

private:
    Window(const Window&);
    Window& operator=(const Window&);

    HWND mHandle;
};


// Factory function
Window MakeWindow()
{
    return Window(CreateWindow());
}


int main()
{

    {
        Window window(MakeWindow());
    }
    std::cout << "Everything is OK." << std::endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

但是,代码运行正常,不会抛出此异常.这是控制台输出:

Window constructor. Handle: 0x1
Window destructor. Handle: 0x1
Everything is OK.
Run Code Online (Sandbox Code Playgroud)

如果我注释掉移动构造函数,则编译失败并出现以下错误:

MysteryMove.cpp: In function 'Window MakeWindow()':
MysteryMove.cpp:39:5: error: 'Window::Window(const Window&)' is private
MysteryMove.cpp:49:33: error: within this context
MysteryMove.cpp: In function 'int main()':
MysteryMove.cpp:39:5: error: 'Window::Window(const Window&)' is private
MysteryMove.cpp:57:35: error: within this context
make: *** [all] Error 1
Run Code Online (Sandbox Code Playgroud)

它似乎没有意义.谁能解释一下发生了什么?

更新

感谢@Philipp,我了解到移动构造函数也可以省略.这在N3126 标准草案的 §12.8/ 34和脚注124中有所描述.

还有人提到RVO仅允许用于非易失性对象.这意味着我可以绕过它来编写像这样的工厂函数:

// Factory function
Window MakeWindow()
{
    volatile Window window(CreateWindow());
    return const_cast<Window&&>(window);
}
Run Code Online (Sandbox Code Playgroud)

确实有效:

Window constructor. Handle: 0x1
Window move constructor. Handle: 0x1
terminate called after throwing an instance of 'int'
Abort trap
Run Code Online (Sandbox Code Playgroud)

Kon*_*lph 9

不是很明显吗?您的代码返回本地临时的副本Window:

Window MakeWindow()
{
    return Window(CreateWindow());
}
Run Code Online (Sandbox Code Playgroud)

编译器将在优化其实这个副本掉(通过return value优化) - 这就是为什么你的移动构造函数实际上从来不叫 - 但是对于正确性的拷贝构造函数仍然必须存在.

  • @Stacked:标准*明确*允许复制(并且,我怀疑,移动)构造函数调用.但是**不允许任何其他功能. (3认同)
  • @Stacked:如果删除了移动构造函数,则源和目标对象是相同的,因此不必使对象无效.见N316中的§12.8/ 34和脚注124. (2认同)