Это мой первый эксперимент с ссылками на С++ 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;
}
Однако код работает отлично, если не будет выбрано это исключение. Это консольный вывод:
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
Это не имеет смысла. Кто-нибудь может объяснить, что происходит?
Update
Благодаря @Philipp я узнал, что конструкторы перемещения также могут быть опущены. Это описано в §12.8/34 и сноске 124 N3126 черновик стандарта.
Также упоминается, что RVO разрешено только для энергонезависимых объектов. Это означает, что я могу обойти это, написав функцию factory следующим образом:
// 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