Should destructors be noexcept?

I'm working on a library which whose classes are almost all hierarchical, problem is that if upmost class destructor is declared noexcept then all destructors in derived classes must also be noexcept, either implicitly or excplicity with noexcept keyword.

Up until now I had such design, but this exception safety brings more problems than benefit.

First problem is that using the library requires a user to make his destructors in derived classed all noexcept, forcing him to write exception free destructors.

Second problem is that, say you derive from such class but your destructor may throw, then you're forced to write destructor similar to this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
SomeClass::~SomeClass()
{
     try
     {
          // Here goes destructor logic which may throw
     }
     catch (...)
     {
         if (std::uncaught_exceptions())
	 {
	      if (IsDebuggerPresent())
              {
		  OutputDebugStringW(L"~SomeClass() threw an exception during stack unwinding\n");
              }
              
              std::exit(-1);
         }
         else
         {
            // Handle exception
         }
     }
}


It's such a problem to write exception free destructors, and especially when you're forced to do so just because library writer made his destructors noexcept.

I've redesigned my destructors to be all noexcept(false), explicity only if there is compiler warning that I should mark it as noexcept(true).

This is so much better because library user can still mark his destructors noexcept if he\she so desires and it can also mark them noexcpt(false), nothing is being forced upon the user.

So my question is, can anyone convience me that destructors marked as noexcept are good for a library code be it LIB or DLL?
Last edited on
It's not a good idea to throw from a destructor. If you do throw an exception in a destructor then that exception must not leave the destructor. If a destructor exits by emitting an exception, all kinds of bad things can happen because the basic rules of the standard library and the language itself will be violated. Also, destructors are by default noexcept (ie non-throwing). We do not expect exceptions from them. std::terminate is called in case of exception going out of destructor.
And what should I do if I call system API in destructor which may throw? but we have no control over the API's.

This means all derived destructors must be noexcept(false) otherwise it won't compile.

otherwise is your declare them as noexcept and handle exception then you're forcing users to make their dtors noexcept as well which is bad.
Last edited on
Any destructor shouldn't throw outside of its destructor. To which system APIs are you referring?

NB. If you really have to call something in a destructor that may throw, you need to have the try/catch clauses within the destructor. You can't not handle it within the destructor that causes the exception. No exception should escape the destructor!

Last edited on
To which system APIs are you referring?

There are many which may throw, for example:
https://learn.microsoft.com/en-us/windows/win32/api/mfidl/nf-mfidl-imfmediasession-close

I call pSession->Close() in destructor ex:

1
2
3
4
5
SomeClass::~SomeClass() noexcept
{
     // C26440 Function is declared noexcept but call Close which may throw
     pSession->Close();
}


Not sure but I think also this one:
https://learn.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-release

I have plenty of such code and is really troublesome to mark destructors as noexcept due to these warnings.

If I omit the noexcept keyword from dtor the warning will change to: "destructor may not throw mark is a noexcept", thus going in circle.

This means I would have to put try catch into a lot of destructors just to be able to get rid of warnings.
Further putting explicit noexcept means users deriving this class will have to make their destructors noexcept as well in order for the code to compile.
Last edited on
This means I would have to put try catch into a lot of destructors just to be able to get rid of warnings.


In a destructor you need try/catch clauses around any code that may throw. As a destructor shouldn't throw outside of it's destructor, then you shouldn't get any exceptions thrown from any called destructors in your destructor code.
OK, you convinced me. try\catch is the answer.

but what should I really do within the catch block?
You only need try/catch within a destructor around code that may throw. Calling a destructor will not throw (as it should handle it's own exceptions). It's not common (IMO) to call a throwing function in a destructor...

but what should I really do within the catch block?


Precisely! You can't throw outside of the destructor so anything you do must be limited to what can be done in that destructor.

Why do you think the functions mentioned above throw an exception? I don't see any mention of throwing an exception in their documentation.

It's not common (IMO) to call a throwing function in a destructor...

Where or how would you then release resources? (if not in destructor)

The sample API releases resources, if not called in destructor then where should it be called? a function that must be called prior destructor maybe.

Why do you think the functions mentioned above throw an exception? I don't see any mention of throwing an exception in their documentation.

Documentation doesn't say but compiler says for Close() method it may throw.

MS docs almost never say whether some function may throw or not, but I don't think the compiler made it up.

Yes, you release resources in a destructor but releasing resources shouldn't usually cause an exception.
Last edited on
ok, thank you for suggestions, I'll revisit my code and put try\catch blocks into destructors.

maybe this will get rid of warnings about noexcept so that I don't have explicitly put the keyword.
Note that noexcept was introduced with C++11. Before that a different way was used - and much of MS code was produced prior to C++11 being available...
There is nothing in the C++ standard that demands that this should generate a warning or error. The behaviour is well defined. If this is just about silencing the warning, and you're OK with std::terminate being called in the rare/unlikely situation when an exception gets thrown, then maybe you should just disable the warning?
Last edited on
There is nothing in the C++ standard that demands that this should generate a warning or error. The behaviour is well defined.

Behavior is well defined and we know whether destructor will be implicitly noexcept or not by knowing the standard, but what we cannot know if whether we accidentally left throwing logic in destructor.

Consider a destructor which is implicitly noexcept(false), but you don't want this to happen, this is where those warnings come handy.

On another side consider a destructor which has no throwing logic but is implicity noexcept(false) not due to it's logic but due inheritance being poisoned, this is where the warning that you should put the noexcept keyword explicitly comes handy, because if you do so you'll get compile time error.

Problem however is that being explicit leads to yet another inheritance poisoning which is excactly my issue, I don't want to disable warning but I also don't want to poisoning, so my solution was to mark upmost class dtor noexcept(false).

But this now again problem because destructor may not throw as we already concluded.

Note that noexcept was introduced with C++11. Before that a different way was used - and much of MS code was produced prior to C++11 being available...

Yes I'm aware of this, with each new cpp standard come new static analysis rules but no updates to MS code.
Also a lot of MS headers still use dynamic throw() instead of noexcept, interesting how MS is forcing rules but does not update their own code.

In the end try\catch sounds like the only sane solution, then noexcept can be put and all warning gone.
Topic archived. No new replies allowed.