我可以抛出unique_ptr吗?

时间:2014-02-11 23:31:00

标签: c++ exception-handling stl unique-ptr

我已经开始使用C ++ 11,特别是使用unique_ptr来使代码异常安全且所有权更具可读性。这通常很有效,直到我想抛出unique_ptr。我有错误代码(抛出许多地方,陷入一个地方),创建复杂的状态。由于逻辑上动态分配的内存的所有权正从thrower传递到catcher,所以unique_ptr似乎是适当的类型来表示并明确catcher已经获得了一个堆对象。没有用,至少使用免费的Visual Studio 2013.这是一个简化的代码示例,它不再像任何有用的东西,但会引发这种行为:

// 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
        ]

我哪里出错了?

2 个答案:

答案 0 :(得分:5)

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

catch(unique_ptr<IError>& Report)

看起来他们在MSVC如何处理复制/移动省略和/或移动语义方面存在差异,我对C ++的处理不够明确,但让我们展示一些可编译的例子。首先是一个带有删除的复制构造函数的基本结构:

#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添加到调用中,我们看到它们都调用了移动构造函数。对于MSVC,我们收到此错误:

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'

有关更复杂的示例,请参阅Throwing movable objects。问题是两年了但我们观察到相同的behavior,因为GCC和Clang在某些情况下都调用了移动构造函数,但MSVC在所有情况下都调用了复制构造函数(GCC和Clang因Throw with object not about to die anyhow (enter non-zero integer而不同一部分。)

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不会。一个糟糕的解决方法是改为抛出一个指针:

throw new unique_ptr<IError>;

catch(unique_ptr<IError>* Report);

答案 1 :(得分:4)

异常的通用规则是“按值抛出,通过const引用捕获”。你不能复制unique_ptr,我猜这是问题的一部分。

从std :: exception派生你的类Error,并抛出它。