4

In C++ you may declare function with exception specification like this:

int foo() const throw(Exception);

I found those two links:

But several things end up unanswered...

Question 1: why to add exception specification? Will it bring any performance increase? What will be different for compiler? Because it seems just like an information for programmer to me.

Question 2: what will happend (what should happen) if I throw something that isn't in specification? For example:

int foo() throw(int) {
        throw char; // Totally unrelated classes, not types in real
}

Question 3: function/method shouldn't throw anything. I found at least two (three, alternative syntax for different compilers) ways to specify no exception throwing:

  • int foo() throw();
  • int foo() __attribute(nothrow)__ for gcc
  • int foo() nothrow for visual C++

Which one is "correct"? Is there any difference? Which one should I use?

Question 4: "standart exceptions", bad_alloc,bad_cast,bad_exception,bad_typeid and ios_base::failure.

Ok bad_alloc is self explaining and I know how (and more importantly when) to use it (add to exception specification), but what about the others? None of them does really ring a bell... Which "code pieces" are they associated with? Like bad_alloc is associated with new char[500000].

Question 5: If I have exception classes hierarchy, like this:

    class ExceptionFileType {
             virtual const char * getError() const = 0;
    };

    class ExceptionFileTypeMissing : public ExceptionFileType {
            virtual const char *getError() cosnt {
                    return "Missing file";
            }
    }

Should I use:

    int foo() throw(ExceptionFileType);

Or:

    int foo() throw(ExceptionFileTypeMissing,ExceptionFileTypeNotWritable,ExceptionFileTypeNotReadable,...)

Note: answers with references would be great. I'm looking for good practice tips.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Vyktor
  • 20,559
  • 6
  • 64
  • 96
  • See [Should I use an exception specifier in C++?](http://stackoverflow.com/questions/88573/should-i-use-an-exception-specifier-in-c). – netcoder Feb 23 '12 at 17:48
  • 1
    Don't use exception specifications, they're useless and deprecated in C++11. The only useful part of that is `throw()`, which has been superseded by `noexcept` in 11. – Cat Plus Plus Feb 23 '12 at 18:21

4 Answers4

6

The simple "good practice" tip is: don't use exception specifications.

Essentially the only exception to that is the possibility of an empty exception specification: throw(). That's sufficiently useful that in C++11 it's been given its own keyword (noexcept). It's generally agreed that any non-empty exception specification is a lousy idea though.

Exception specifications (other than noexcept) are officially deprecated -- and unlike many deprecated features, removing this would affect little enough source code that I think there's a good chance it really will eventually be removed (certainly no guarantee, but a pretty fair chance anyway).

As for what happens when/if you do throw an exception of a type not allowed by the exception specification: std::unexpected() gets invoked. By default, that invokes terminate(). You can use std::set_unexpected to set your own handler -- but about all you can reasonably do is add some logging before you terminate(). Your unexpected handler is not allowed to return.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • I'm not sure about the "never been used much". The functions in `std::exception` had them, which meant that anyone deriving their own exception classes had to use them. (Admittedly `throw()`.) – James Kanze Feb 23 '12 at 17:53
  • @JamesKanze: Thanks -- I've rephrased it to be more accurate. – Jerry Coffin Feb 23 '12 at 17:58
  • Can you provide some reference for "It's generally agreed that any non-empty exception specification is a lousy idea though."? My project leader won't accept "answer on stackoverflow" as reliable source (although your reputation is pretty impressive) :-/ – Vyktor Feb 23 '12 at 18:24
  • @Vyktor: Because it's cumbersome to write and doesn't really protect you from anything. – Cat Plus Plus Feb 23 '12 at 18:26
  • One possibility would be [Guru of the Week #82](http://www.gotw.ca/gotw/082.htm) (Herb Sutter). – Jerry Coffin Feb 23 '12 at 18:29
  • @CatPlusPlus I like this answer very much, but if Jerry could provide some article or link or something what I could show to my teamleader and add into documentation I'll be glad to have it consistent and "official". – Vyktor Feb 23 '12 at 18:29
  • @Vyktor: I'm not sure if you've seen the mention of GotW above, but if not, it's a worthwhile read (not just as backing for this answer). Boost's standards also advise: "Avoid exception-specifications.", giving the following rationale. I'd also think that §D.4 of the new standard: "The use of dynamic-exception-specifications is deprecated." might qualify as the best support of all (about as official as you can get, anyway). – Jerry Coffin Feb 23 '12 at 19:50
  • @JerryCoffin thank you for your effort I'm already reading provided link (GotW) it'll be used for good :P – Vyktor Feb 23 '12 at 20:02
  • @Vyktor: The definitive reference for C++03 is _C++ Coding Standards_, Item 75. "Avoid exception specifications." – Philipp Feb 23 '12 at 21:10
  • "Your unexpected handler is not allowed to return." - true, but it *is* allowed to throw an exception, and the program can continue if that is handled. Termination is not inevitable. – Mike Seymour Feb 24 '12 at 00:42
3

Question 1

Don't bother. They were a bad idea, and were deprecated in the latest version of the language. They give no benefit to the compiler, since they are checked at runtime; if anything, they might hurt performance in some cases.

Question 2

A function called std::unexpected will be called. By default, this calls std::terminate; by default, that terminates the program. Both of these behaviours can be changed, using std::set_unexpected and std::set_terminate to install your own handler, if you really want to.

Question 3

throw() was the standard way to do it; the others are non-portable compiler extensions. In C++11, you might use noexcept, which gives a compile-time check that nothing can throw, rather than a run-time check that nothing does throw.

Question 4

  • bad_cast is thrown when a dynamic_cast of a reference fails.
  • bad_exception is thrown in some weird circumstances when an exception specification is violated.
  • bad_typeid is thrown if evaluating the argument to typeid involves dereferencing a null pointer
  • ios_base::failure is thrown by the input/output library (<iostream> etc.) when some operations fail

Question 5

If you want to allow the entire heirarchy to be thrown, then just specify the base class. But you shouldn't be using exception specifiers at all.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
2

First, let's be very clear what an exception specification does: it's more or less like an assert that can't be disabled, asserting that you will not exit the function with an exception other than the ones missing. As such, it's utility is far more limited than it would seem at first; for the most part (and in this case, I can't imagine an exception), the only really useful guarantee is throw(), which guarantees that no exception will be thrown; if you want to write exception safe code, you'll need this guarantee for a few low level functions.

In practice, although throw() could allow some additional compiler optimizations, the generic implementation tends to result in less efficient code when an exception specification is used. In C++11, the throw() has been replaced by noexcept, presumably with the hope that compiler implementers will do something intelligent with it.

EDIT:

Since everyone (including myself) seems to have missed your question 4:

bad_alloc will be thrown by the operator new function if it cannot allocate the memory.

bad_cast will be thrown by a dynamic_cast to a reference, in the case where the cast fails. (A dynamic_cast to a pointer returns a null pointer in such cases.)

bad_exception will be thrown when an exception specification is violated, provided the exception specification allows bad_exception. (In other words, forget it.)

bad_typeid will be thrown if you try to use typeid with a null pointer.

ios_base::failure will be thrown if you request a stream to throw in case of errors.

Practically speaking: you'll catch bad_alloc if you want to recover and continue from out of memory situations. Which means not very often. (It's very, very difficult to recover from an out of memory situation.) For bad_cast, it's probably preferrable to use pointers, and test for null, if you aren't sure. And there's no excuse for ever seeing a bad_typeid. Most of the time, you'll probably want to test IO errors explicitly, rather than configuring the stream to throw exceptions; and exception when ios_base::badbit is set might be an exception (since it represents a truly exceptional case of a hardward fault).

James Kanze
  • 150,581
  • 18
  • 184
  • 329
1

Questions 1 and 2 are addressed at some length in this question.

Questions 3 and 5 are covered by the recommendation, in the accepted answer to that question, that you don't use exception specifications at all.

Question 4 seems to be adequately addressed by typing those exception names into the search engine of your choice, or consulting the index of a good C++ book. Do you have a specific query about them?

Community
  • 1
  • 1
Useless
  • 64,155
  • 6
  • 88
  • 132