我可以抛出一个unique_ptr吗? [英] Can I throw a unique_ptr?

查看:198
本文介绍了我可以抛出一个unique_ptr吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经开始使用C ++ 11,特别是使用unique_ptr来使代码异常安全和所有权更易于阅读。这通常很好,直到我想要抛出一个unique_ptr。我有错误代码(抛出了很多地方,捕获在一个地方)创建复杂的状态。由于逻辑上动态分配的内存的所有权正在从投掷者转移到捕获器,所以unique_ptr似乎是适当的类型,表明捕获者已经获取了堆对象。至少与免费的Visual Studio 2013不起作用。这是一个简单的代码示例,不再像任何有用的东西,而是引发了这种行为:

I have started using C++ 11 and in particular using unique_ptr liberally to make code exception-safe and ownership more readable. This has generally worked well until I wanted to throw a unique_ptr. I have error code (thrown many places, caught in a single place) that creates complex state. Since the ownership of that dynamically allocated memory logically is being transferred from the thrower to the catcher, unique_ptr seemed like the appropriate type to indicate that and make it clear that the catcher had acquired a heap object. Didn't work, at least with the free Visual Studio 2013. Here's a boiled-down code example that no longer resembles anything useful but elicits the behavior:

// cl /nologo /EHsc /W4 test1.cpp
#include <memory>
using std::unique_ptr;
class IError
    {
public:
    virtual ~IError() {};
    virtual void DoStuff();
    };

unique_ptr<IError> Error();

int Foo() { throw Error(); }

int main(void)
    {
    try {
        Foo();
        }
    catch(unique_ptr<IError> Report)
        {
        Report->DoStuff();
        }
    return 0;
    }

编译器如此反应:

test1.cpp
test1.cpp(13) : warning C4673: throwing 'std::unique_ptr<IError,std::default_delete<_Ty>>' the following types will n
ot be considered at the catch site
        with
        [
            _Ty=IError
        ]
test1.cpp(13) : warning C4670: '_Unique_ptr_base<class IError,struct std::default_delete<class IError>,1>' : this bas
e class is inaccessible
test1.cpp(13) : error C2280: 'std::unique_ptr<IError,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,
std::default_delete<_Ty>> &)' : attempting to reference a deleted function
        with
        [
            _Ty=IError
        ]
        C:\bin\Visual Studio Express 2013\VC\INCLUDE\memory(1486) : see declaration of 'std::unique_ptr<IError,std::d
efault_delete<_Ty>>::unique_ptr'
        with
        [
            _Ty=IError
        ]

我在哪里出错?

推荐答案

作为代理我将使用具有MSVC版本18.00.21005.1的 Rextester 。对于GCC 4.8.1和Clang 3.5,我将使用 Coliru 。现在,最初我提出了一个仓促的回答,我说'code> unique_ptr 不能复制,所以你应该通过引用来捕捉它们。但是,当您在MSVC中抛出该对象时,会出现错误。所以上述建议只适用于GCC和Clang。

As a surrogate I'll be using Rextester which has MSVC version 18.00.21005.1. For GCC 4.8.1 and Clang 3.5, I'll be using Coliru. Now, initially when giving a hasty answer, I said that unique_ptrs cannot be copied and so you should be catching them by reference. However it appears the error occurs when you throw the object in MSVC. So the above advice will only apply to GCC and Clang.

catch(unique_ptr<IError>& Report)

看起来,它们在MSVC如何处理复制/移动elision和/或移动语义方面有所不同,我在C ++中不够好,比这更具体,但是我们来展示一些可编辑的例子。首先是一个删除的拷贝构造函数的基本结构:

It appears that they differ in how MSVC handles copy/move elision and/or move semantics, I'm not good enough at C++ to be more specific than that, but let's show some compilable examples. First a basic struct with a deleted copy constructor:

#include <iostream>
struct D {
    D() {};
    D(const D& other) = delete;
    D(D&& other) { std::cout << "call D move constructor... \n"; }
};

int main()
{
    try {
        throw D();
    } catch(D const& d)
    {   
    }
}

无论优化级别如何,对于GCC和Clang,除非您还在调用中添加 -fno-elide-constructors ,否则不会输出,我们看到它们都调用move构造函数。对于MSVC,我们得到这个错误:

Regardless of optimization level, for both GCC and Clang, no output unless you also add -fno-elide-constructors to the invocation and we see that they both call the move constructor. For MSVC, we get this error:

source_file.cpp(22) : error C2280: 'D::D(const D &)' : attempting to reference a deleted function
        source_file.cpp(7) : see declaration of 'D::D'

有关更复杂的示例,请参阅投掷可移动对象。问题是两岁,但是我们在GCC和Clang两者都观察到相同的行为在某些情况下,移动构造函数,但MSVC在所有情况下调用复制构造函数(GCC和Clang对于不同,对象不会死亡(输入非零整数部分。)

For a more complicated example, see Throwing movable objects. The question is two years old yet we observe the same behavior in that GCC and Clang both call the move constructor in certain situations but MSVC calls the copy constructor in all situations (GCC and Clang differ for the Throw with object not about to die anyhow (enter non-zero integer part.)

Throw directly: 
C
caught: 007FFA7C
~
Throw with object about to die anyhow
C
c
~
caught: 007FFA74
~
Throw with object not about to die anyhow (enter non-zero integer)
C
c
caught: 007FFA70
~
1
~






TL; DR GCC和Clang将编译它,但MSVC将不会。抛出一个指针:


TL;DR GCC and Clang will compile it but MSVC won't. A crappy workaround is to throw a pointer instead:

throw new unique_ptr<IError>;

catch(unique_ptr<IError>* Report);

这篇关于我可以抛出一个unique_ptr吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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