我已经开始使用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
]
我哪里出错了?
答案 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,并抛出它。