14.4 — Uncaught exceptions, catch-all handlers, and exception specifiers

By now, you should have a reasonable idea of how exceptions work. In this lesson, we’ll cover a few more interesting exception cases.

Uncaught exceptions

In the past few examples, there are quite a few cases where a function assumes its caller (or another function somewhere up the call stack) will handle the exception. In the following example, mySqrt() assumes someone will handle the exception that it throws -- but what happens if nobody actually does?

Here’s our square root program again, minus the try block in main():

Now, let’s say the user enters -4, and mySqrt(-4) raises an exception. Function mySqrt() doesn’t handle the exception, so the program stack unwinds and control returns to main(). But there’s no exception handler here either, so main() terminates. At this point, we just terminated our application!

When main() terminates with an unhandled exception, the operating system will generally notify you that an unhandled exception error has occurred. How it does this depends on the operating system, but possibilities include printing an error message, popping up an error dialog, or simply crashing. Some OSes are less graceful than others. Generally this is something you want to avoid altogether!

Catch-all handlers

And now we find ourselves in a conundrum: functions can potentially throw exceptions of any data type, and if an exception is not caught, it will propagate to the top of your program and cause it to terminate. Since it’s possible to call functions without knowing how they are even implemented (and thus, what type of exceptions they may throw), how can we possibly prevent this from happening?

Fortunately, C++ provides us with a mechanism to catch all types of exceptions. This is known as a catch-all handler. A catch-all handler works just like a normal catch block, except that instead of using a specific type to catch, it uses the ellipses operator (…) as the type to catch.

If you recall from lesson 7.14 on ellipses and why to avoid them, ellipses were previously used to pass arguments of any type to a function. In this context, they represent exceptions of any data type. Here’s an simple example:

Because there is no specific exception handler for type int, the catch-all handler catches this exception. This example produces the following result:

We caught an exception of an undetermined type

The catch-all handler should be placed last in the catch block chain. This is to ensure that exceptions can be caught by exception handlers tailored to specific data types if those handlers exist. Visual Studio enforces this constraint -- I am unsure if other compilers do. (Per reader Lonami in the comments below, GCC does too).

Often, the catch-all handler block is left empty:

This will catch any unanticipated exceptions and prevent them from stack unwinding to the top of your program, but does no specific error handling.

Using the catch-all handler to wrap main()

One interesting use for the catch-all handler is to wrap the contents of main():

In this case, if runGame() or any of the functions it calls throws an exception that is not caught, that exception will unwind up the stack and eventually get caught by this catch-all handler. This will prevent main() from terminating, and gives us a chance to print an error of our choosing and then save the user’s state before exiting. This can be useful to catch and handle problems that may be unanticipated.

Optional reading

Dynamic exception specifiers

This subsection should be considered optional reading because exception specifiers are rarely used in practice and have been removed from C++ in C++17 and C++20.

Exception specifiers are a mechanism that allows us to use a function declaration to specify whether a function may or will not throw exceptions. This can be useful in determining whether a function call needs to be put inside a try block or not.

There are three types of exception specifiers, all of which use what is called the throw (…) syntax.

First, we can use an empty throw statement to denote that a function does not throw any exceptions outside of itself:

Note that doSomething() can still use exceptions as long as they are handled internally. Any function that is declared with throw() is supposed to cause the program to terminate immediately if it does try to throw an exception outside of itself, but implementation is spotty.

Second, we can use a specific throw statement to denote that a function may throw a particular type of exception:

Finally, we can use a catch-all throw statement to denote that a function may throw an unspecified type of exception:

Due to the incomplete compiler implementation, the fact that exception specifiers are more like statements of intent than guarantees, some incompatibility with template functions, and the fact that most C++ programmers are unaware of their existence, I recommend you do not bother using dynamic exception specifiers. They were deprecated in C++11, and have been removed from the language in later versions.


C++11 added a fourth exception specifier that is actually getting some use: noexcept. Noexcept is a exception specifier that is used to indicate that a function can not throw an exception. Semantically, it allows you to see at a glance that a function will not throw an exception. It also potentially enables some compiler optimizations. Destructors are generally implicitly noexcept (as they can’t throw an exception). If a noexcept function does try to throw an exception, then std::terminate is called to terminate the application.

Proper application of noexcept is non-trivial, and probably warrants its own lesson, so we’ll leave it here -- as a mention that it exists, but as a topic for advanced users to explore further.

14.5 -- Exceptions, classes, and inheritance
14.3 -- Exceptions, functions, and stack unwinding

36 comments to 14.4 — Uncaught exceptions, catch-all handlers, and exception specifiers

  • Mohammed

    Why do I return 1 in the last code from the main function? Shouldn't it be return 0?

    • nascardriver

      The `return 1` was supposed to be in the catch block. I updated the lesson. Thanks for pointing it out!
      EDIT: nevermind, it was correct is written. `runGame` is an infinite loop. `main` only returns if an error occurs.

  • Abdelrahman

    Thanks for this content it is really helpful.
    I have a question when using ellipsis to catch all exceptions, is there any possible way to use the va_list, va_start, va_arg, va_end stuff to infer the type of the exception thrown or not ?

    • nascardriver

      No, you need to know the type of the exception beforehand. You can rethrow the exception to another handler that tries all kinds of types that may be thrown (See example at ), but you can't get the type automatically.

  • Hassan Muhammad

    Hi Alex,
       I've been one year off and had some revision afterwards, guess what, everything is now back on track(thumbs up for your hardwork).
       By the way, i think i've a firm grasp of this chapter(up to this point though), but is there a way to identify the kind(say data type) of the exception caught by "catch-all block"?

    • nascardriver


      If you don't know the type of something, you can't do anything with it. Exceptions are thrown at run-time, where types no longer exist (for the most part), so you can't determine the type there.
      If an exception is a derivative of `std::exception`, you can catch it as such.
      Otherwise, you can use `std::current_exception` to forward the unknown exception to a function where you use `std::rethrow_exception` and try to catch all possible types. There is no way to catch by `auto` or similar.

  • David

    Do you have a source for Bjarne Stroustrup considering exception specifiers a failed experiment? I'd be interested in reading that. I read his book "The Design and Evolution of C++" and in the chapter on exceptions he briefly mentions exception specifiers as an idea for making exceptions part of the interface of a function so callers could know what exceptions to expect, but I don't believe he mentioned any dislike for this idea or even if it made it into C++ (apparently it did!)

    • Alex

      I can't find the citation I used when I wrote the article, so I've removed the quote. The article doesn't rely on that for justification anyway. The fact that dynamic exception specifiers have been removed from the language as of C++17 is justification enough of them being a failed experiment!

  • Asgar

    Hi Alex,

    About exception specifiers, isn't there a 4th type, in addition to the 3 mentioned?

    1. throw()
    2. throw() with a particular data type
    3. throw(...)
    and then
    4. noexcept

    You discourage the use of the first 3. Isn't the 4th one used quite a lot, as of C++11?

    • Alex

      Yes, noexcept was added in C++11 and this lesson hadn't been updated since then. I've added a short blurb about it to the lesson, and added it to my to do list to come back and do the topic justice.

  • Trevor29

    Hi Alex
    Is there any way of saving the exception value for later use in the catch all exception catch(...)? I am thinking of code that would print an error message and perhaps do some tidying up, then throw the exception again for the calling code to deal with (or not!).
    It seems to me that leaving exceptions unhandled for higher level code to handle could be a good way of generating memory leaks if calls to destructors are bypassed.

    PS: I have read ahead to Section 14.6 which covers rethrowing exceptions. Does "throw" without a parameter work in this case?

    • Alex

      Yes, throw without a parameter is what you'd want to use in this case. Using this, you can have an exception handler do some cleanup and then "pass the buck" up the call stack for someone else to deal with.

      Exceptions are a one good reason to avoid doing your own local memory management within a function -- if you hit an exception (or early return) somewhere between the allocation and deallocation, the deallocation will never happen. There are ways around this though, namely smart pointers (covered in chapter 15), which use RAII principles to ensure destruction happens even if exceptions or early returns or other things happen between the allocation and deallocation...

  • Rev

    Just a comment about fact of exception specifiers. I by chance opened a library file of DevC++ and saw the creators added throw() to the constructors of the auto_ptr class. So what you said that not many programmers knew about it is not quite true :)

  • Peter Baum

    I'm actually back at the end of chapter 6 and just finished my blackjack program.  I ended up here because I needed exception handling for error checking purposes.  In particular, gracefully catching conversion errors from stoi() and similar situations.  It seems to me that the sections on exception handling are placed way too late in the lesson sequence.  By the time a student reaches the end of chapter 6, they should be programming relatively sophisticated programs, and the basics of exception handling should certainly be taught before objects. You know... IMHO.

    • Alex

      It's always a struggle to determine the best point to introduce topics.

      Many programmers don't use exceptions at all (the Google C++ style guide advises Google employees not to use them). So part of the calculus here is that it's one of C++'s more optional features, and thus should occur later in the tutorial, even if it might be useful.

      Nevertheless, I appreciate the feedback.

  • pingsoli

    Exception sepcifiers 'throw()' is deprecated, using noexcept will be more elegant. :)

    By the way, I really like your post. Thanks a lot.

  • Hardik

    The above segment gives me an error("expected type-specifier before '...' token.")
    Should I conclude that it isn't supported by my compiler?

  • Ninja

    I kind of forgot the reasons for this (if it was explained in an earlier lesson) but why do you end statements with std::endl when they start with std::cerr but use '\n' to end statements starting with std::cout? I always use '\n' for everything. It doesn't matter right?

    • Alex

      std::cerr is unbuffered, so it's always flushed immediately. Using std::endl (which causes both a newline and a flush) is a bit extraneous, in that the flush isn't needed. But since std::cerr should (hopefully) be called only sporadically, we don't need to worry about performance. So it ultimately boils down to a stylistic choice. std::endl reminds me that it will always be output immediately.

  • Vlad

    Could we use the error handling as a switch ()? Would it be more efficient, or is this heresy? Something like this:

    This won't compile, though: expected primary expression before 'catch', but it's just an example.

    • Vlad

      Besides that I just wrote this ad-hoc (void instead of double), I realized I forgot to add try{}. It works, but I still wonder if it can be used in real life?

      • Alex

        I think you could. Exceptions were designed as a way to pass errors from a function up the stack, so a higher-level function can handle the error. Passing a negative value to a sqrt function is definitely an error, and having the caller handle the error in some way that makes sense does seem reasonable.

    • Alex

      You could I suppose, though I've never seen this in practice. Exceptions are inherently not all that performant, so you'd definitely be better off using an actual switch.

  • "Visual Studio enforces this constraint -- I am unsure if other compilers do."

    GCC does too! Can be disallowed with -fpermissive though

  • Alice

    Hi Alex,

    I have two questions.

    (1) In the first example, you do not have the return value for main. I also tried it and it did not give a compiling error. I wonder why. Shouldn't there be a return value since we specify int main()?

    (2) Is there anyway to tell the value from thrown statement instead of data type? For example, can I throw all int values and in the corresponding catch statement, we do a if-else or switch-case kind of thing to work on the values from thrown. Because I was thinking we will run out the use of data type as the amount is limited.


    • Alex

      1) Unintentional omission. I've fixed the example. Visual Studio is permissive about not having a return statement at the end of main, but it really shouldn't be.
      2) Yes, you can access the value through the variable in the catch statement, and switch based on that:

  • DOBRESCU Mihai


    I love your sense of humour.

    // Look ma, no exception handler!
    cout << "The sqrt of " << dX << " is " << MySqrt(dX) << endl;

  • octavsly

    I wonder why throwing with an anonymous variable can be caught with a reference to the variable...

    throw 5.0 ;
    catch (double &dX)
    cout << "We caught an exception of type double: " << dX << endl;

    Probably throw creates a variable... It is just strange to see.

    • Alex

      Yup. When you throw an exception, the throw expression creates a temporary object, called an exception object, that is then passed to the appropriate catch block. Unlike other temporary objects, the exception object is treated as an lvalue, and thus can be caught by non-const reference.

  • vidya


    How to catch exceptions occurred in DLL.
    For example
    calling the "A.DLL" (DLL(donot know the source code) is generating unhandled exception, access violation)

    How I do catch it in my main program so that I can terminate it properly


    • Alex

      My understanding is that throwing exceptions across DLL boundaries is generally a bad idea.

      It only works if the DLL is compiled using exactly the same C++ compiler version as your code, and you need to ensure the same runtime library is used for both client and DLL.

      Generally, it's better to use some other method of returning errors across DLL boundaries, like error codes.

  • Peter

    Hi there,

    I'm just learning c/c++. I am from a Java background and very used to a method specifying the Exceptions it is throwing. How would you know what Exceptions to expect from a function in c++ then? Hope that the function is well documented? Otherwise wouldn't you always have to check for the general exception?!

    Thank you, Peter

    • Kiena

      As an analogy, you can think of c++ exceptions as java runtime exceptions or errors.

      In java, errors and runtime exceptions can optionally be specified in the signature as well, but handling them is never mandatory. In c++, by specifying them, you could enforce exception checking, but even compilers may not support this, according to the tutorial. Many developers consider checked exceptions a mistake in the java language too (probably because of their misuse even in the standard api).

Leave a Comment

Put all code inside code tags: [code]your code here[/code]