从 C++ 异常的构造函数中抛出异常

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

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

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

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

例子:假设我想把所有可能的异常分为两种类型,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 对象被捕获通过封闭的处理程序。 GCC and Clang both agree.

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

请注意,如果在异常的处理期间调用的复制构造函数通过异常退出,则将调用std::terminate即, 因为异常对象被复制到按值捕获的处理程序中)(see example)。这与您描述的情况不同。