I have a class whose ctor makes a driver call, and whose dtor makes the matching terminating/release driver call. Those calls can fail. The problem is naturally with the dtor.
I am naturally aware of the common wisdom of avoiding exceptions in dtors, since, if you throw one during stack unwinding, you get std::terminate. But - I would rather not just "swallow" such errors and fail to report them - if I can. So, is it legitimate/idiomatic to write code saying:
~MyClass() noexcept(false) {
auto result = something_which_may_fail_but_wont_throw();
if (std::uncaught_exceptions() == 0) {
throw some_exception(result);
}
}
Or is this just baroque and not a good idea?
Note: This class does not have access to the standard output/error streams, nor a log etc.
If the only thing you do is check whether
uncaught_exceptions()is zero, then you can miss some cases where it's safe to propagate the exception. For example, considerHere,
ywill be destroyed during stack unwinding. DuringY's destructor, there is one uncaught exception in flight. The destructor creates anXobject whose destructor subsequently has to decide whether to throw aFooException. It is safe for it to do so, because there will be an opportunity to catch theFooExceptionbefore it gets to a point wherestd::terminatewill be invoked. ButX::~Xdetermines that an uncaught exception is in flight, so it decides not to throw the exception.There is nothing technically wrong with this, but it's potentially confusing that the behaviour of the try-catch block in
Y::~Ydepends on the context from whichY::~Ywas invoked. Ideally,X::~Xshould still throw the exception in this scenario.N4152 explains the correct way to use
std::uncaught_exceptions:In the above example,
X::X()needs to store the value ofstd::uncaught_exceptions()during its construction, which will be 1. Then, the destructor will see that the value is still 1, which means that it's safe to let an exception escape from the destructor.This technique should only be used in situations where you really need to throw from destructors and you are fine with the fact that whatever purpose will be served by throwing from the destructor will go unfulfilled if the
std::uncaught_exceptions()check fails (forcing the destructor to either swallow the error condition or terminate the program). This rarely is the case.