【发布时间】:2011-01-22 11:45:13
【问题描述】:
这是我对 C++0x 右值引用的第一次实验,似乎正在发生一些奇怪的事情。
在下面的代码示例中,工厂函数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;
}
但是,代码运行良好,没有抛出此异常。这是控制台输出:
Window constructor. Handle: 0x1
Window destructor. Handle: 0x1
Everything is OK.
如果我注释掉移动构造函数,则编译失败并出现以下错误:
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
这似乎没有意义。谁能解释一下是怎么回事?
更新
感谢@Philipp,我了解到移动构造函数也可以省略。这在 §12.8/34 和 N3126 draft standard 的脚注 124 中有描述。
这里还提到 RVO 只允许用于非易失性对象。这意味着我可以像这样编写工厂函数来绕过它:
// Factory function
Window MakeWindow()
{
volatile Window window(CreateWindow());
return const_cast<Window&&>(window);
}
确实有效:
Window constructor. Handle: 0x1
Window move constructor. Handle: 0x1
terminate called after throwing an instance of 'int'
Abort trap
【问题讨论】:
-
试试
g++ -fno-elide-constructors