Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exception specifications when deriving from std::exception in C++11

I have an exception class as follows:

#include <exception>

struct InvalidPathException : public std::exception
{
   explicit InvalidPathException() {}
   const char* what() const;
};

const char*
InvalidPathException::what() const {
    return "Path is not valid";
}

When compiling under GCC 4.4 with -Wall -std=c++0x

error: looser throw specifier for 'virtual const char* InvalidPathException::what() const'

error: overriding 'virtual const char* std::exception::what() const throw ()'

Quite right, too, since I'm overriding std::exception's what() method that does indeed have a throw() exception specifier. But as one will often be informed, we shouldn't use exception specifiers. And as I understand it, they are deprecated in C++11, but evidently not yet in GCC with -std=c++0x.

So I'd be interested in the best approach for now. In the code I'm developing I do care about performance and so worry about the oft-mentioned overhead of throw(), but in reality is this overhead so severe? Am I right in thinking that I'd only suffer it when what() is actually called, which would only be after such an exception is thrown (and likewise for the other methods inherited from std::exception that all have throw() specifiers)?

Alternatively, is there a way to work around this error given by GCC?

like image 319
beldaz Avatar asked Nov 26 '10 04:11

beldaz


1 Answers

Empty throw specifications are useful, as they actually enable compiler optimizations at the caller's site, as Wikipedia knows (I don't have a technical quote handy).

And for reasons of optimization opportunities, nothrow-specifications are not deprecated in the upcoming standard, they just don't look like throw () any more but are called noexcept. Well, yes, and they work slightly differently.

Here's a discussion of noexcept that also details why traditional nothrow-specifications prohobit optimizations at the callee's site.

Generally, you pay for every throw specification you have, at least with a fully compliant compiler, which GCC has, in this respect, appearantly not always been. Those throw specifications have to be checked at run-time, even empty ones. That is because if an exception is raised that does not conform to the throw specification, stack unwinding has to take place within that stack frame (so you need code for that in addition to the conformance check) and then std::unexpected has to be called. On the other hand, you potentially save time/space for every empty throw specification as the compiler may make more assumptions when calling that function. I chicken out by saying only a profiler can give you the definitive answer of whether your particular code suffers from or improves by (empty!) throws specification.

As a workaround to your actual problem, may the following work?

  • Introduce #define NOTHROW throw () and use it for your exception's what and other stuff.
  • When GCC implements noexcept, redefine NOTHROW.

Update

As @James McNellis notes, throw () will be forward-compatible. In that case, I suggest just using throw () where you have to and, apart from that, if in doubt, profile.

like image 173
dennycrane Avatar answered Sep 23 '22 02:09

dennycrane