230

What is the technical reason why it is considered bad practice to use the C++ throw keyword in a function signature?

bool some_func() throw(myExc)
{
  ...
  if (problem_occurred) 
  {
    throw myExc("problem occurred");
  }
  ...
}
Mark Lakata
  • 19,989
  • 5
  • 106
  • 123
Konstantin
  • 6,061
  • 8
  • 40
  • 48
  • See this recent related question: http://stackoverflow.com/questions/1037575/why-arent-exceptions-in-c-checked-by-the-compiler – laalto Jun 28 '09 at 18:10
  • And http://stackoverflow.com/questions/88573/should-i-use-an-exception-specifier-in-c – akauppi Sep 11 '09 at 11:05
  • 1
    Does `noexcept` change anything? – Aaron McDaid Aug 18 '15 at 12:08
  • 15
    There is nothing wrong with having opinions about something programming related. This closing criteria is bad, at least for this question. It is an interesting question with interesting answers. – Anders Lindén Mar 28 '16 at 10:58
  • 1
    It should be noted [exception specifications](http://en.cppreference.com/w/cpp/language/except_spec) are deprecated since C++11. – François Andrieux Apr 05 '18 at 18:37
  • And why are exception specifications in Java not regarded as bad practice, and why does C++ not, for these reasons, take over that concept? – cwellm May 19 '23 at 04:31

7 Answers7

141

No, it is not considered good practice. On the contrary, it is generally considered a bad idea.

http://www.gotw.ca/publications/mill22.htm goes into a lot more detail about why, but the problem is partly that the compiler is unable to enforce this, so it has to be checked at runtime, which is usually undesirable. And it is not well supported in any case. (MSVC ignores exception specifications, except throw(), which it interprets as a guarantee that no exception will be thrown.

jalf
  • 243,077
  • 51
  • 345
  • 550
  • 26
    Yes. There are better ways of adding whitespace to your code than throw (myEx). – Assaf Lavie Jun 28 '09 at 18:36
  • It's not a matter of being unable to enforce it at compile time. Java compilers can enforce it, after all. It's that it's not supposed to be enforced at compile time. The function is completely allowed to throw other exceptions, but if it does, then unexpected() must be called. – Rob Kennedy Jun 28 '09 at 20:48
  • Er, unless you mean C++ compilers are unable to enforce it because the standard forbids them from doing so. – Rob Kennedy Jun 28 '09 at 20:50
  • 4
    yeah, people who just discover the exception specifications often assume that they work like in Java, where the compiler is able to enforce them. In C++, that won't happen, which makes them a lot less useful. – jalf Jun 28 '09 at 21:23
  • 11
    But how about the documentative purpose then? Also, you will be told which exceptions you will never catch even if you try. – Anders Lindén Mar 28 '16 at 11:03
  • 1
    @AndersLindén what documentative purpose? If you just want to document the behavior of your code, just put a comment above it. Not sure what you mean with the second part. Exceptions you will never catch even if you try? – jalf Apr 01 '16 at 10:09
  • 8
    I think code is powerful when it comes to documenting your code. (comments can lie). The "documentative" effect I am referring to is that you will know for sure which exceptions you can catch and others cannot be. – Anders Lindén Apr 01 '16 at 10:14
  • 1
    @AndersLindén, It should be noted that the problem of exception specifications is that they are a too heavy price for documenting purposes. The exceptions you will never catch (even if you try) still can be thrown. As you mentioned, they cannot be caught - the event can't be handled in an elegant way. As for exception specifications, a code can lie much more guileful than comments. – Arthur P. Golubev Aug 01 '17 at 01:04
  • re: "comments can lie". Well, so can throw declarations. – xdavidliu Jul 25 '22 at 14:55
65

Jalf already linked to it, but the GOTW puts it quite nicely why exception specifications are not as useful as one might hope:

int Gunc() throw();    // will throw nothing (?)
int Hunc() throw(A,B); // can only throw A or B (?)

Are the comments correct? Not quite. Gunc() may indeed throw something, and Hunc() may well throw something other than A or B! The compiler just guarantees to beat them senseless if they do… oh, and to beat your program senseless too, most of the time.

That's just what it comes down to, you probably just will end up with a call to terminate() and your program dying a quick but painful death.

The GOTWs conclusion is:

So here’s what seems to be the best advice we as a community have learned as of today:

  • Moral #1: Never write an exception specification.
  • Moral #2: Except possibly an empty one, but if I were you I’d avoid even that.
sth
  • 222,467
  • 53
  • 283
  • 367
  • 1
    I'm not sure why would I throw an exception and wouldn't be able to mention it. Even if it's thrown by another function I do know what exceptions could be thrown. The only reason I can see is because it's tedious. – MasterMastic Jun 09 '13 at 10:05
  • 4
    @Ken: The point is that writing exception specifications has mostly negative consequences. The only positive effect is that it shows the programmer what exceptions can occur, but since it's not checked by the compiler in a reasonable way it's prone to errors and therefore not worth a lot. – sth Jun 09 '13 at 15:42
  • 1
    Oh okay, thanks for responding. I guess that's what documentation is for. – MasterMastic Jun 09 '13 at 18:11
  • 2
    Not correct. Exception specification should be written, but the idea is to communicate what errors the caller should try to catch. – ABCD Sep 23 '16 at 02:03
  • 1
    Just as @StudentT says: it is the function's responsibility to guarantee not to throw further any other exceptions. If they do, the program terminates as it should. To declare throw means *it is not my responsibility to handle this situation* and the caller should have enough information to do this. Not declaring exceptions means they can occur anywhere and they could be handled anywhere. That is certainly anti-OOP mess. It is design failure to catch exceptions on wrong places. I'd recommend not to throw empty, since exceptions are exceptional and most function should throw empty anyway. – Jan Turoň Feb 17 '17 at 19:27
36

To add a bit more value to all the other answer's to this question, one should invest a few minutes in the question: What is the output of the following code?

#include <iostream>
void throw_exception() throw(const char *)
{
    throw 10;
}
void my_unexpected(){
    std::cout << "well - this was unexpected" << std::endl;
}
int main(int argc, char **argv){
    std::set_unexpected(my_unexpected);
    try{
        throw_exception();
    }catch(int x){
        std::cout << "catch int: " << x << std::endl;
    }catch(...){
        std::cout << "catch ..." << std::endl;
    }
}

Answer: As noted here, the program calls std::terminate() and thus none of the exception handlers will get called.

Details: First my_unexpected() function is called, but since it doesn't re-throw a matching exception type for the throw_exception() function prototype, in the end, std::terminate() is called. So the full output looks like this:

user@user:~/tmp$ g++ -o except.test except.test.cpp
user@user:~/tmp$ ./except.test
well - this was unexpected
terminate called after throwing an instance of 'int'
Aborted (core dumped)

Micha Wiedenmann
  • 19,979
  • 21
  • 92
  • 137
John Doe
  • 2,746
  • 2
  • 35
  • 50
14

The only practical effect of the throw specifier is that if something different from myExc is thrown by your function, std::unexpected will be called (instead of the normal unhandled exception mechanism).

To document the kind of exceptions that a function can throw, I typically do this:

bool
some_func() /* throw (myExc) */ {
}
Paolo Tedesco
  • 55,237
  • 33
  • 144
  • 193
  • 5
    It's also useful to note that a call to std::unexpected() usually results in a call to std::terminate() and the abrupt end of your program. – sth Jun 28 '09 at 18:28
  • 1
    - and that MSVC at least does not implement this behavior as far as I know. – jalf Jun 28 '09 at 19:14
10

Well, while googling about this throw specification, I had a look at this article :- (http://blogs.msdn.com/b/larryosterman/archive/2006/03/22/558390.aspx)

I am reproducing a part of it here also, so that it can be used in future irrespective of the fact that the above link works or not.

   class MyClass
   {
    size_t CalculateFoo()
    {
        :
        :
    };
    size_t MethodThatCannotThrow() throw()
    {
        return 100;
    };
    void ExampleMethod()
    {
        size_t foo, bar;
        try
        {
            foo = CalculateFoo();
            bar = foo * 100;
            MethodThatCannotThrow();
            printf("bar is %d", bar);
        }
        catch (...)
        {
        }
    }
};

When the compiler sees this, with the "throw()" attribute, the compiler can completely optimize the "bar" variable away, because it knows that there is no way for an exception to be thrown from MethodThatCannotThrow(). Without the throw() attribute, the compiler has to create the "bar" variable, because if MethodThatCannotThrow throws an exception, the exception handler may/will depend on the value of the bar variable.

In addition, source code analysis tools like prefast can (and will) use the throw() annotation to improve their error detection capabilities - for example, if you have a try/catch and all the functions you call are marked as throw(), you don't need the try/catch (yes, this has a problem if you later call a function that could throw).

Pratik Singhal
  • 6,283
  • 10
  • 55
  • 97
9

When throw specifications were added to the language it was with the best intentions, but practice has borne out a more practical approach.

With C++, my general rule of thumb is to only use throw specifications to indicate that a method can't throw. This is a strong guarantee. Otherwise, assume it could throw anything.

Greg D
  • 43,259
  • 14
  • 84
  • 117
3

A no throw specification on an inlined function that only returns a member variable and could not possibly throw exceptions may be used by some compilers to do pessimizations (a made-up word for the opposite of optimizations) that can have a detrimental effect on performance. This is described in the Boost literature: Exception-specification

With some compilers a no-throw specification on non-inline functions may be beneficial if the correct optimizations are made and the use of that function impacts performance in a way that it justifies it.

To me it sounds like whether to use it or not is a call made by a very critical eye as part of a performance optimization effort, perhaps using profiling tools.

A quote from the above link for those in a hurry (contains an example of bad unintended effects of specifying throw on an inline function from a naive compiler):

Exception-specification rationale

Exception specifications [ISO 15.4] are sometimes coded to indicate what exceptions may be thrown, or because the programmer hopes they will improve performance. But consider the following member from a smart pointer:

T& operator*() const throw() { return *ptr; }

This function calls no other functions; it only manipulates fundamental data types like pointers Therefore, no runtime behavior of the exception-specification can ever be invoked. The function is completely exposed to the compiler; indeed it is declared inline Therefore, a smart compiler can easily deduce that the functions are incapable of throwing exceptions, and make the same optimizations it would have made based on the empty exception-specification. A "dumb" compiler, however, may make all kinds of pessimizations.

For example, some compilers turn off inlining if there is an exception-specification. Some compilers add try/catch blocks. Such pessimizations can be a performance disaster which makes the code unusable in practical applications.

Although initially appealing, an exception-specification tends to have consequences that require very careful thought to understand. The biggest problem with exception-specifications is that programmers use them as though they have the effect the programmer would like, instead of the effect they actually have.

A non-inline function is the one place a "throws nothing" exception-specification may have some benefit with some compilers.

Community
  • 1
  • 1
Pablo Adames
  • 578
  • 6
  • 12
  • 1
    "The biggest problem with exception-specifications is that programmers use them as though they have the effect the programmer would like, instead of the effect they actually have." This is the #1 reason for errors, when communicating with machines or people: the difference between what we say and what we mean. – Thagomizer May 17 '19 at 22:58