when things have gone horribly wrong and there isn’t any graceful way of dealing with the problem, the terminate() function acts as a last resort.
As its name suggests, by default, the remedy the method offers is simply ending the program. In the default implementation, it will do so by calling abort(). Stack unwinding or object destruction are not guaranteed – It is completely up to the compiler implementation. So if your code depends on the object destructor for any cleanups, such as erasing temporary files, calling terminate() might leave you cleaning the mess on your own.
Obviously calling terminate () doesn’t make a whole lot of sense in a normal process flow. However, it is not difficult to inadvertently create a situation where it is called indirectly. Depending on the compiler you are using and the build type (debug or production) your process might simply wink out of existence with no apparent reason.
In this post, I’ll review a few scenarios with C++ 14 where throwing an exception at the wrong place and time invokes terminate().
Throwing an exception without a proper exception handler :
In this example, The program is throwing a base class exception object but catching a subclass. Since downcasting is not implicit and there is no catch clause to handle an std::exception, the program ends by calling terminate().
Throwing an exception from a function declared with noexcept:
A function with a noexcept specification mustn’t throw an exception . The stress here is on must not there is nothing preventing it or any of the other function it’s calling to throw, However, if the exception is not caught, in the function scope, terminate is called:
Throwing an exception from a destructor:
User defined destructors are implicitly declared with noexcept , causing them to call terminate as described above.
throwing and exception in the constructor or the destructor of a global, static or thread-local objects:
Assuming the class :
and any of these instantiations of A
Will cause the program to terminate before main() is even called.
Normally constructors are allowed to throw exceptions. But statics, global and thread-local objects are initialized before the main() function starts, and destroyed after it exits. So exceptions thrown while initializing or destroying these objects cannot have an in-program code catching and handling making terminate() the only logical option.
Throwing an exception from a thread function:
Since a thread has its own stack, an exception handler clause must exist in the context of the thread to correctly unwind the stack once an exception is thrown. So failing to have an exception handler in the thread function is similar to having no handler at all causing the program to end with terminate():
Thre are other ways terminate can be unwittingly triggered. Such as changing the default std::atexit handler to a user defined method and throwing and exception for it or re-trowing an exception you never received But, in my view, they are less prone to happen without putting some effort into it.
For futher reading see chapter 15.5.1 of the C++ standard document: C++14 – ISO/IEC 14882:2014, C++ 11 – ISO/IEC 14882:2011