从 C++ 中异常的构造函数引发异常

Throwing an exception from the constructor of an exception in C++

本文关键字:异常 构造函数 C++      更新时间:2023-10-16

请注意,这个问题与从异常类的构造函数而不是任何旧构造函数中抛出异常有关。 我无法在堆栈溢出上找到重复的问题。

本文建议不要抛出这样的异常,但我对给出的技术原因持怀疑态度(作者似乎在评论中回溯了原因(。

我将举一个例子,然后讨论我对正在发生的事情的解释,这意味着这样做基本上没有问题,至少从技术角度来看是这样。 我的主要问题是我的解释是否正确,或者我在概念上是否遗漏了一些东西。

示例:假设我想将所有可能的异常分为两种类型,User_ErrorCoder_Error。 前者显然表明用户做了一些他们不应该做的事情,后者表明一些严重的内部检查失败了,有人应该提交错误报告。 这是一个(显然过于简化的(草图:

#include <stdexcept>
#include <string>
...
class Coder_Error : public std::runtime_error
{
public:
Coder_Error( const std::string& msg ) :
std::runtime_error( msg + "  Please freak out and file a bug report!" )
{}
};

class User_Error : public std::runtime_error
{
protected:
static void check_state() const
{
...  // May rely on static members of this class or other classes.
if ( validation_failed ) {
throw Coder_Error( "A useful description of the problem." );
}
}
public:
User_Error( const std::string& msg ) : std::runtime_error( msg )
{
check_state();
}
};

现在假设我在User_Error::check_state()会投掷的情况下throw User_Error( "Some useful message." );。 会发生什么?

解释:我的期望是,Coder_Error对象将在遇到throw User_Error( "Some useful message." )行中的throw之前被抛出,因此我们不必担心同时抛出两个异常,或者类似的事情。 为了更明确地说,我希望相关步骤按以下顺序进行。

  1. User_Error::User_Error会被召唤。

  2. User_Error::check_state会被召唤。

  3. Coder_Error::Coder_Error会被召唤。

  4. 步骤 3 中创建的Coder_Error对象将被抛出。

  5. 堆栈展开将开始,普通执行将停止,因此执行永远不会达到可以throw步骤 1 中创建的User_Error的程度。 (我假设没有捕获任何错误。

这是对的吗?

据我所知,您是正确的,评估throw User_Error(...)有一个明确定义的结果并且不会调用std::terminate,前提是Coder_Error对象被封闭处理程序捕获。海湾合作委员会和Clang都同意。

对于 C++14 及以下,您的推理是正确的。在 C++17 中,由于强制复制 elision,User_Error的构造函数在抛出异常的过程中被调用,一旦编译器已经开始计算throw部分并为异常对象分配了一些存储空间。但是,当构造通过第二个异常退出时,编译器会清理该存储并继续查找第二个异常的处理程序。无论哪种情况,结果都如您所说。

请注意,如果在处理异常期间调用的复制构造函数通过异常退出(即,当异常对象被复制到按值捕获的处理程序中时(,将调用std::terminate(请参阅示例(。这与你所描述的情况不同。