I have a class whose destructor is noexcept(false)
. I know it only throws under certain circumstances, and I want to use it as a member variable of a class with a noexcept
destructor. From https://en.cppreference.com/w/cpp/language/function-try-block I read "Every exception thrown from any statement in the function body, or (for constructors) from any member or base constructor, or (for destructors) from any member or base destructor, transfers control to the handler-sequence the same way an exception thrown in a regular try block would." which makes me think this should be correct:
#include <exception>
class ConditionallyThrowingDtor {
public:
bool willThrow = true;
ConditionallyThrowingDtor() = default;
~ConditionallyThrowingDtor() noexcept(false) {
if (willThrow) {
throw std::exception();
}
}
};
class NonThrowingDtor {
public:
ConditionallyThrowingDtor x;
~NonThrowingDtor() noexcept try {
x.willThrow = false;
} catch (...) {
// Ignore because we know it will never happen.
}
};
int main() {
// ConditionallyThrowingDtor y; // Throws on destruction as expected.
NonThrowingDtor x;
}
https://godbolt.org/z/ez17fx (MSVC)
My understanding of noexcept
and the function-try-block on ~NonThrowingDtor()
is that noexcept
guarantees it won't throw (and that it does this by essentially doing try { ... } catch (...) { std::terminate(); }
https://en.cppreference.com/w/cpp/language/noexcept_spec. But the function-try-block with catch (...)
and no additional throw should guarantee it never throws. Clang is OK with this, but as the godbolt link shows, MSVC says
<source>(23): warning C4297: 'NonThrowingDtor::~NonThrowingDtor': function assumed not to throw an exception but does
<source>(23): note: destructor or deallocator has a (possibly implicit) non-throwing exception specification
~NonThrowingDtor() noexcept try {
x.willThrow = false;
} catch (...) {
// Ignore because we know it will never happen.
}
is "wrong" as equivalent to
~NonThrowingDtor() noexcept try {
x.willThrow = false;
} catch (...) {
throw;
}
so simply
~NonThrowingDtor() noexcept
{
x.willThrow = false;
}
To not propagate exception, you have to use explicitly return
:
~NonThrowingDtor() noexcept try {
x.willThrow = false;
} catch (...) {
return; // Required to not propagate exception.
}
Unfortunately, msvc still warns with this form which doesn't throw.
(On the other side, clang/gcc don't warn for implicit (but do for explicit) throw
in that case).