Should I use an exception specifier in C++?

In C++, you can specify that a function may or may not throw an exception by using an exception specifier. For example:

void foo() throw(); // guaranteed not to throw an exception
void bar() throw(int); // may throw an exception of type int
void baz() throw(...); // may throw an exception of some unspecified type

I'm doubtful about actually using them because of the following:

  1. The compiler doesn't really enforce exception specifiers in any rigorous way, so the benefits are not great. Ideally, you would like to get a compile error.
  2. If a function violates an exception specifier, I think the standard behaviour is to terminate the program.
  3. In VS.Net, it treats throw(X) as throw(...), so adherence to the standard is not strong.

Do you think exception specifiers should be used?
Please answer with "yes" or "no" and provide some reasons to justify your answer.


Solution 1:

No.

Here are several examples why:

  1. Template code is impossible to write with exception specifications,

    template<class T>
    void f( T k )
    {
         T x( k );
         x.x();
    }
    

    The copies might throw, the parameter passing might throw, and x() might throw some unknown exception.

  2. Exception-specifications tend to prohibit extensibility.

    virtual void open() throw( FileNotFound );
    

    might evolve into

    virtual void open() throw( FileNotFound, SocketNotReady, InterprocessObjectNotImplemented, HardwareUnresponsive );
    

    You could really write that as

    throw( ... )
    

    The first is not extensible, the second is overambitious and the third is really what you mean, when you write virtual functions.

  3. Legacy code

    When you write code which relies on another library, you don't really know what it might do when something goes horribly wrong.

    int lib_f();
    
    void g() throw( k_too_small_exception )
    { 
       int k = lib_f();
       if( k < 0 ) throw k_too_small_exception();
    }
    

    g will terminate, when lib_f() throws. This is (in most cases) not what you really want. std::terminate() should never be called. It is always better to let the application crash with an unhandled exception, from which you can retrieve a stack-trace, than to silently/violently die.

  4. Write code that returns common errors and throws on exceptional occasions.

    Error e = open( "bla.txt" );
    if( e == FileNotFound )
        MessageUser( "File bla.txt not found" );
    if( e == AccessDenied )
        MessageUser( "Failed to open bla.txt, because we don't have read rights ..." );
    if( e != Success )
        MessageUser( "Failed due to some other error, error code = " + itoa( e ) );
    
    try
    {
       std::vector<TObj> k( 1000 );
       // ...
    }
    catch( const bad_alloc& b )
    { 
       MessageUser( "out of memory, exiting process" );
       throw;
    }
    

Nevertheless, when your library just throws your own exceptions, you can use exception specifications to state your intent.

Solution 2:

Avoid exception specifications in C++. The reasons you give in your question are a pretty good start for why.

See Herb Sutter's "A Pragmatic Look at Exception Specifications".

Solution 3:

I think the standardly except convention (for C++)
Exception specifiers were an experiment in the C++ standard that mostly failed.
The exception being that the no throw specifier is useful but you should also add the appropriate try catch block internally to make sure the code matches the specifier. Herb Sutter has a page on the subject. Gotch 82

In a addition I think it is worth describing Exception Guarantees.

These are basically documentation on how the state of an object is affected by exceptions escaping a method on that object. Unfortunately they are not enforced or otherwise mentioned by the compiler.
Boost and Exceptions

Exception Guarantees

No Guarantee:

There is no guarantee about the state of the object after an exception escapes a method
In these situations the object should no longer be used.

Basic Guarantee:

In nearly all situations this should be the minimum guarantee a method provides.
This guarantees the object's state is well defined and can still be consistently used.

Strong Guarantee: (aka Transactional Guarantee)

This guarantees that the method will complete successfully
Or an Exception will be thrown and the objects state will not change.

No Throw Guarantee:

The method guarantees that no exceptions are allowed to propagate out of the method.
All destructors should make this guarantee.
| N.B. If an exception escapes a destructor while an exception is already propagating
| the application will terminate

Solution 4:

gcc will emit warnings when you violate exception specifications. What I do is to use macros to use the exception specifications only in a "lint" mode compile expressly for checking to make sure the exceptions agree with my documentation.