移动构造函数似乎没有执行 [英] Move constructor seemingly not executed

查看:106
本文介绍了移动构造函数似乎没有执行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我对C ++ 0x右值引用的第一个实验,并且有些奇怪的事情正在发生。



在下面的代码示例中, $ c> MakeWindow 按值返回一个Window对象。调用者使用它来初始化一个Window对象。如果我理解正确,这应该调用移动构造函数。为了检测这个,我抛出一个异常。除此之外,我禁用了复制构造函数:

  #include< iostream> 


//假的WinAPI
typedef void * HWND;
HWND CreateWindow(){return(void *)1; }
void DestroyWindow(HWND){}
//结束WinAPI


// C ++ WinAPI Wrapper库
类Window
{
public:
Window(HWND inHandle):
mHandle(inHandle)
{
std :: cout< Window constructor。Handle:< inHandle<< std :: endl;
}

窗口(窗口&& rhs):
mHandle(rhs.mHandle)
{
std :: cout< Window move constructor。Handle:< mHandle< std :: endl;
rhs.mHandle = 0;
throw 1; //这是我的断点
}

〜Window()
{
std :: cout< Window destructor。Handle:< mHandle< std :: endl;
if(mHandle)
{
DestroyWindow(mHandle);
}
}

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

HWND mHandle;
};


//工厂函数
窗口MakeWindow()
{
return Window(CreateWindow());
}


int main()
{

{
窗口窗口(MakeWindow
}
std :: cout<< 一切都好。 << std :: endl;
return 0;
}

然而,代码运行正常,没有抛出这个异常。这是控制台输出:

 窗口构造函数。句柄:0x1 
窗口析构函数。句柄:0x1
一切正常。

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

  MysteryMove.cpp:在函数'Window MakeWindow()':
MysteryMove.cpp:39:5:error:'Window :: Window (const Window&)'是私有的
MysteryMove.cpp:49:33:error:在这个上下文中
MysteryMove.cpp:在函数'int main()':
MysteryMove.cpp: 39:5:错误:'Window :: Window(const Window&)'是私人
MysteryMove.cpp:57:35:错误:在此上下文中
make:*** [all] Error 1

似乎没有意义。



感谢@Philipp我学习了move constructors也可以被省略。这在 N3126标准草案的§12.8/ 34和脚注124中有所描述



它还提到RVO只允许用于非易失性对象。这意味着我可以这样写工厂函数:

  //工厂函数
窗口MakeWindow
{
volatile窗口窗口(CreateWindow());
return const_cast< Window&&>(window);
}

确实有效:

 窗口构造函数。句柄:0x1 
窗口移动构造函数。句柄:0x1
抛出一个'int'实例后调用终止
中止陷阱


解决方案

这不是很明显吗?您的代码会返回本地临时 c>窗口 c:

副本



窗口MakeWindow()
{
return Window(CreateWindow());
}

编译器事实上会优化这个拷贝(通过返回值优化)这就是为什么你的移动构造函数从未实际调用 - 但为了正确性,一个副本构造函数仍然必须存在。


This is my first experiment with C++0x rvalue references and something strange seems to be going on.

In the code sample below the factory function MakeWindow returns a Window object by value. The caller uses it to initialize a Window object. If I understood correctly, this should invoke the move constructor. In order to detect this I throw an exception there. On top of that I disabled the copy constructor:

#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;
}

However the code runs fine without this exception being thrown. This is the console output:

Window constructor. Handle: 0x1
Window destructor. Handle: 0x1
Everything is OK.

If I comment out the move constructor then compilation fails with the following errors:

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

It doesn't seem to make sense. Can anyone explain what is going on?

Update

Thanks to @Philipp I learned that move constructors can also be omitted. This is described in §12.8/34 and footnote 124 of the N3126 draft standard.

It is there also mentioned that RVO is only allowed for non-volatile objects. This means I can get around it writing the factory function like this:

// Factory function
Window MakeWindow()
{
    volatile Window window(CreateWindow());
    return const_cast<Window&&>(window);
}

And indeed it works:

Window constructor. Handle: 0x1
Window move constructor. Handle: 0x1
terminate called after throwing an instance of 'int'
Abort trap

解决方案

Isn’t it obvious? Your code returns a copy of the local temporary Window:

Window MakeWindow()
{
    return Window(CreateWindow());
}

The compiler will in fact optimize this copy away (via return value optimization) – this is why your move constructor is never actually called – but for correctness a copy constructor must still be present.

这篇关于移动构造函数似乎没有执行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆