Search code examples
c++exceptionconstructorthrow

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.

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. 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." ); in a case where User_Error::check_state() will throw. 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. 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.

  2. User_Error::check_state will get called.

  3. Coder_Error::Coder_Error will get called.

  4. The Coder_Error object created in step 3 will get thrown.

  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.)

Is this correct?


Solution

  • 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. GCC and Clang both agree.

    Your reasoning is correct for C++14 and below. In C++17, due to mandatory copy elision, the constructor of User_Error is called during the process of throwing the exception, i.e, once the compiler has already started evaluating the throw part and has allocated some storage somewhere for the exception object. 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 (i.e., as the exception object is being copied into a handler that catches by value) (see example). This is distinct from the situation you have described.