Herb Sutter wrote on this topic: http://www.gotw.ca/gotw/047.htm
Its conclusion is to never throw from the destructor, always report an error using a mechanism that you will use in the case when you cannot throw.
Two reasons:
- It doesnβt always work. Sometimes
uncaught_exception returns true, and yet it is safe to throw. - it is a poor design to have the same error reported in two different ways, both of which the user will have to consider if they want to know about the error.
Note that for any given reusable code snippet, there is no way to know for sure that it will never be called during the unwinding of the stack. No matter what your code does, you cannot be sure that any user will want to invoke it from the destructor using try/catch to handle its exceptions. Therefore, you cannot rely on uncaught_exception , which always returns true if it is safe to throw, except, perhaps by documenting a function, "cannot be called from destructors." If you resorted to this, then all subscribers would also have to document their functions, "should not be called from destructors," and you have even more annoying restrictions.
Among other things, the nothrow guarantee is valuable to users - it helps them write code that excludes code if they know that the particular thing they are doing will not drop.
One way is to give your class a close member function that calls trySomeCleanupOperation and throws if it fails. The destructor then calls trySomeCleanupOperation and writes or suppresses the error, but does not throw. Then users can call close if they want to know if their operation was successful or not, and just let the destructor handle it if they are not interested (including the case when the destructor is called as part of the stack expansion, because an exception was thrown before moving to the user call close ). βAha!β You say, βbut that defeats the RAII goal, because the user must remember to call close !β. Yes, a little, but the question is whether RAII is doing everything you want. It's impossible. The question is whether it will consistently do less than you would like (you want it to throw an exception if trySomeCleanupOperator does not work), or less unexpectedly when used while unwinding packets.
Also, throwing exceptions for all failed statements is an important part of my unit testing.
Probably a mistake - your unit testing platform should be able to handle terminate() as a test failure. Suppose the statement fails when the stack fails - of course you want to write this, but you cannot do this by throwing an exception, so you draw yourself in the corner. If your statements cease, you may find them as completion.
Unfortunately, if you are done, you will not be able to run the rest of the tests (at least not in this process). But if the statement fails, then generally speaking, your program is in an unknown and potentially dangerous state. Therefore, once you lose the statement, you still cannot rely on doing anything else in the process. You might consider developing your own test environment to use several processes, or simply accepting that a sufficiently serious test failure would hamper the rest of the tests. Outwardly to the test structure, you might think that your test run has three possible results: "everything went well, something failed, the tests crashed." If the test run is not completed, you do not consider it as a pass.