简体   繁体   English

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

[英]Throwing an exception from the constructor of an exception in C++

Note that this question has to do with throwing an exception from the constructor of an exception class , not from any old constructor. 请注意,此问题与从异常类的构造函数不是从任何旧的构造函数引发异常有关 I was not able to find a duplicate question on stackoverflow. 我在stackoverflow上找不到重复的问题。

This article recommends against throwing such an exception, but I am suspicious of the technical reason given (and the author seems to backtrack on the reason in the comments). 本文建议不要抛出这样的异常,但是我对给出的技术原因感到怀疑(作者似乎在评论中回溯了原因)。

I will give an example and then discuss my interpretation of what's happening, which implies that there basically shouldn't be a problem with doing this, at least from a technical point of view. 我将举一个例子,然后讨论我对正在发生的事情的解释,这意味着至少从技术角度来看,这样做基本上应该没有问题。 My main question is whether my interpretation is correct, or if I am missing something conceptually. 我的主要问题是我的解释是否正确,或者我在概念上是否缺少某些东西。

Example: Suppose that I want to divide all possible exceptions into two types, User_Error and Coder_Error . 示例:假设我想将所有可能的异常分为两种类型, User_ErrorCoder_Error The former indicates, obviously, that the user did something that they weren't supposed to, and the latter indicates that some serious internal check failed and that someone should file a bug report. 很明显,前者表明用户做了他们不应该做的事情,而后者表明某些严重的内部检查失败了,有人应该提交错误报告。 Here is a (obviously oversimplified) sketch: 这是一个(显然过于简化的)草图:

#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();
    }
};

Now suppose that I do throw User_Error( "Some useful message." ); 现在假设我确实throw User_Error( "Some useful message." ); in a case where User_Error::check_state() will throw. 如果User_Error::check_state()将抛出。 What will happen? 会发生什么?

Interpretation: My expectation would be that the Coder_Error object will get thrown before the throw in throw User_Error( "Some useful message." ) line is ever encountered, and therefore we do not have to worry about two exceptions being thrown simultaneously, or anything like that. 解释:我的期望是Coder_Error对象会在throw User_Error( "Some useful message." )行之前被throw ,因此我们不必担心同时抛出两个异常或类似的东西那。 To be more explicit, I expect the relevant steps to occur in something like the following order. 更明确地说,我希望相关步骤按以下顺序进行。

  1. User_Error::User_Error will get called. User_Error::User_Error将被调用。

  2. User_Error::check_state will get called. User_Error::check_state将被调用。

  3. Coder_Error::Coder_Error will get called. Coder_Error::Coder_Error将被调用。

  4. The Coder_Error object created in step 3 will get thrown. 在第3步中创建的Coder_Error对象将被抛出。

  5. Stack unwinding will commence and ordinary execution will be stopped, so the execution will never get to a point where it could throw the User_Error created in step 1. (I am assuming that no errors are getting caught.) 堆栈展开将开始,并且常规执行将停止,因此该执行将永远不会到达可能throw在步骤1中创建的User_Error 。(我假设不会捕获任何错误。)

Is this correct? 这个对吗?

As far as I can tell, you're correct that evaluating throw User_Error(...) has a well-defined result and does not call std::terminate , provided that the Coder_Error object is caught by an enclosing handler. 据我所知,您正确的是,对throw User_Error(...)评估具有明确定义的结果,并且不会调用std::terminate ,只要Coder_Error对象被封闭的处理程序捕获即可。 GCC and Clang both agree . GCC和Clang都同意

Your reasoning is correct for C++14 and below. 您的推理对C ++ 14及以下版本是正确的。 In C++17, due to mandatory copy elision, the constructor of User_Error is called during the process of throwing the exception, ie, once the compiler has already started evaluating the throw part and has allocated some storage somewhere for the exception object. 在C ++ 17,由于强制复制省略的构造User_Error抛出异常, 即,一旦编译器已经开始评估的过程中,所谓的throw一部分,并分配给地方一些存储的异常对象。 However, when the construction exits via second exception, the compiler cleans up that storage and proceeds to look for a handler for the second exception. 但是,当通过第二个异常退出构造时,编译器将清理该存储,并继续查找第二个异常的处理程序。 In either case, the result is as you say. 无论哪种情况,结果都如您所说。

Note that std::terminate will be called if the copy constructor called during handling of the exception exits via an exception ( ie, as the exception object is being copied into a handler that catches by value) ( see example ). 请注意,如果在处理异常期间调用的复制构造函数通过异常退出( 即,异常对象被复制到按值捕获的处理程序中),则将调用std::terminate请参见示例 )。 This is distinct from the situation you have described. 这与您描述的情况不同。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM