11

Take a look at this code which causes program to terminate without catching the exception.

#include <iostream>
#include <string>
#include <memory>
#include <stdexcept>
using namespace std;

struct test {
  ~test() noexcept(false) {
    throw runtime_error("-my-cool-exception-");
  }
};

int main()
{
  try {
    auto ptr = unique_ptr<test>(new test());
    //test t; // this is ok, without unique_ptr<test> it works fine.
  }
  catch(exception& e) {
    cout << "this is not called, the program is aborted";
    cout << e.what() << endl;
  }
  return 0;
}

This question is different from stack overflow question: throwing exceptions out of destructor.

The difference is that only when I use unique_ptr<test> the exception is not caught.

You can see the live code, edit and compile here http://cpp.sh/9sk5m

Patryk
  • 22,602
  • 44
  • 128
  • 244
amin
  • 3,672
  • 5
  • 33
  • 61
  • 6
    Because 20.10.1.2.2 requires that the deletion doesn't throw exceptions. You're violating the library preconditions. – Kerrek SB Jun 13 '16 at 11:13
  • I have library which totally depends on this, can't break it. Is there a workaround for this? thanks. – amin Jun 13 '16 at 11:14
  • 9
    Workaround: fix your library, do it now before [it bites you any further](http://stackoverflow.com/a/130123/3426025). – BeyelerStudios Jun 13 '16 at 11:15
  • 5
    If you throw an exception from a destructor, your program cannot continue. An exception means "stop whatever you are doing and jump to the nearest handler", but you cannot stop destroying an object, it just makes no sense. When you stop *making* an object, i.e. throw from a constructor, you destroy parts that are already made, the language takes care of that. But what on earth would it mean to stop destroying an object? Re-make the destroyed parts? That's impossible. Leave it in a half-destroyed state? There are no half-destroyed objects in C++. The only thing you can sensibly do is abort. – n. m. could be an AI Jun 13 '16 at 11:20
  • @n.m. throwing an exception from a `noexcept(false)` destructor is okay. But very dangerous – in case stack unwinding was triggered by an exception itself. – Ven Jun 13 '16 at 11:22
  • 1
    @Ven it is technically OK, that is, not forbidden by the standard, but logically your object is in an ill-defined state. – n. m. could be an AI Jun 13 '16 at 11:29

1 Answers1

15

That's required by the standard. As per unique.ptr.single.dtor, 20.10.1.2.2.1:

Requires: The expression get_deleter()(get()) shall be well formed, shall have well-defined behavior, and shall not throw exceptions. [ Note: The use of default_delete requires T to be a complete type. — end note ]

(emphasis mine)

So really, it doesn't matter whether you destructor itself has noexcept(false) or not. It's forbidden to throw in this case – final word.

This is the case in general the case std:: – except when specified otherwise. As per res.on.functions, 17.6.4.8.2.4:

In particular, the effects are undefined in the following cases: [...] if any replacement function or handler function or destructor operation exits via an exception, unless specifically allowed in the applicable Required behavior: paragraph.

(emphasis mine)

Note: In general, you can have throwing destructors with noexcept(false). However, this is very dangerous, as std::terminate will be called if the stack was unwinding due to a thrown exception. As per except.ctor, 15.2.1:

As control passes from the point where an exception is thrown to a handler, destructors are invoked by a process, specified in this section, called stack unwinding. If a destructor directly invoked by stack unwinding exits with an exception, std::terminate is called ([except.terminate]). [ Note: Consequently, destructors should generally catch exceptions and not let them propagate out of the destructor. — end note ]

(emphasis mine)

See it live on Coliru.

Ven
  • 19,015
  • 2
  • 41
  • 61
  • 1
    It should be noted that even if you give *your* destructor `noexcept(false)`, that doesn't change the fact that `unique_ptr`'s destructor remains `noexcept`. So the exception will be swallowed there. – Nicol Bolas Jun 13 '16 at 14:00
  • "swallowed" implies it disappears. It doesn't. – Ven Jun 13 '16 at 14:35
  • OK, "explode" is probably a more accurate term than "swallow" ;) But either way, you're not going to see that exception reach your `catch` statement, no matter how much you use `noexcept(false)`. – Nicol Bolas Jun 13 '16 at 14:57
  • @NicolBolas added a sentence to that effect. – Ven Jun 13 '16 at 14:58
  • 3
    In addition to the `unique_ptr`-specific rule, any destructor operation invoked by the standard library must not throw exceptions except when specifically allowed ([\[res.on.functions\]/2](http://eel.is/c++draft/res.on.functions#2).4). – T.C. Jun 14 '16 at 03:39
  • @T.C. Thanks! Added it to the answer. – Ven Jun 14 '16 at 08:38