1

I am compiling a C++ library, and I have an issue with destructors and noexcept.

It is my understanding that, since C++11, the default value of noexcept(...) inside destructors has changed. In particular, now destructors default to noexcept (that is noexcept(true)).

Now, I have a library for which I get many compiler warnings due to the fact that the destructor of some classes can throw, but it's not marked noexcept(false). So I decided to change that, and add the specification. However, these classes inherited from some common parent Base, so this forced me to add it also to the parent class destructor, otherwise I get the compiler error overriding ‘virtual Base::~Base() noexcept.

Of course, I can add noexcept(false) also to the base class (actually, it is enough to add it there, as far as I understand), but there are lots of classes that inherit from Base, and they would all inherit the noexcept(false) attribute (as far as I understand). However, the base class destructor itself will never throw, and only few of the derived classes can actually throw. Therefore, it appears a waste to mark the base class destructor noexcept(false) only for a few classes.

So, I have two questions:

1) Is there any way around this? I cannot remove the throws in the few derived classes that throw, since they are important.

2) I am not sure how much the compiler optimization could be degraded due to the addition of noexcept(false) to the base class of (virtually) all the classes in the library. When (if ever) should this concern me?

Edit: on a side note, is there a way to change the default value for noexcept inside a project? Perhaps a compiler option?

Edit: since there have been many feedback, I owe an edit, with a few remarks:

1) I did not write the library, and I certainly do not plan to rewrite the destructors of many classes. Having to add a noexcept(false) to a some base classes is already enough of a pain. And resorting to a different library is not an option (for different reasons that go beyond programming).

2) When I said "I cannot remove the throws in the few derived classes that throw, since they are important" I meant that I believe the code should terminate in those case, since something really bad happened and undefined behavior may follow anyways. The throw at least comes with a little explanation of what happened.

3) I understand that throwing in a destructor of a derived class is bad, since it may leak due to the fact that the parent class' destructor is not called (is this correct?). What would be a clean way around if one ought to terminate the program (to avoid obscure bugs later) and still let the user know what happened?

bartgol
  • 1,703
  • 2
  • 20
  • 30
  • 2
    *"...I cannot remove the `throws` in the few derived classes that throw, since they are important."* - Sounds like a bad poorly and bad badly designed Library. [Throwing from the Destructor](http://stackoverflow.com/questions/130117/throwing-exceptions-out-of-a-destructor?rq=1) is dangerous. – WhiZTiM Aug 11 '16 at 02:23
  • 1
    If possible, try to change the destructors so that they don't throw. Throwing an exception from a destructor is dangerous because destructors can be called during stack unwinding, when another exception is in flight (thrown but not yet caught). Throwing an exception while another is in flight will terminate the program. – Wyzard Aug 11 '16 at 02:24
  • 4
    "*I cannot remove the throws in the few derived classes that throw, since they are important.*" Yes, you can. You simply don't want to make the change to actually *do that*. But the fact is you are most assuredly capable of doing it. You may have designed yourself into a corner, but you can always design yourself out of it again. – Nicol Bolas Aug 11 '16 at 02:35
  • 2
    "*the base class of (virtually) all the classes in the library*" If you have one type which is the base of "virtually" every class in your library... something may have gone terribly wrong. That sounds like Java-style design, not C++. – Nicol Bolas Aug 11 '16 at 02:53
  • Let me rephrase it: sure, I can remove the throws, but a) that would require a lot of work, and b) I'm not really sure there would be no side effects. So it's not that they are important (poor word choice) it's that the library is huge and I'm afraid of possible drawbacks, which I am not able to fully predict. – bartgol Aug 11 '16 at 02:54
  • 2
    A destructor that can let an exception escape it will eventually cause program crashes. If you're the creator of the library you owe it to your users to stop emitting exceptions from your destructors so you won't eventually crash their programs. If you're the user of the library *get a new library*. – Mark B Aug 11 '16 at 02:54
  • @NicolBolas Not my design. It's a library I'm using, but I wanted to avoid all the compilation errors. I like clean builds, if possible. – bartgol Aug 11 '16 at 02:55

2 Answers2

2

If the destructor is virtual, the derived destructors can't be noexcept(false) unless the base destructor is also noexcept(false).

Think about it: the point of virtual functions is that the derived class can be called even if the caller only knows about the base class. If something calls delete on a Base*, the call may be compiled without any exception-handling code if Base promises that its destructor won't throw exceptions. If the pointer points to a Derived instance whose destructor actually does throw an exception, that would be bad.

Try to change the derived destructors so they don't throw exceptions. If that's not possible, add noexcept(false) to the base destructor.

Wyzard
  • 33,849
  • 3
  • 67
  • 87
  • Thanks for clarifying why you should not have a `noexcept(false)` in the derived class' destructor unless you have it also in the parent class' destructor. Makes sense. – bartgol Aug 11 '16 at 03:05
2

I am not sure how much the compiler optimization could be degraded due to the addition of noexcept(false) to the base class of (virtually) all the classes in the library. When (if ever) should this concern me?

You should not be concerned about compiler optimizations on the basis of noexcept declarations on destructors. Most of the optimizations around noexcept declarations come from code that detects that an operation is noexcept and does something more efficient based on that. That is, explicit metaprogramming.

Why doesn't that matter for destructors? Well, consider the C++ standard library. If you hand vector a type, and one of the destructors of the contained object throws, you get undefined behavior. This is true for everything in the standard library (unless explicitly noted).

Which means the standard library will not bother to check if your destructor is noexcept or not. It can, and almost certainly will, assume that no destructor emits an exception. And if one does... well, that's your fault.

So your code will likely not run slower just because you use noexcept(false) on a destructor. Your decision of whether to use noexcept(false) on the base class's destructor should not be affected by performance concerns.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982