Jerry said: ...the result isn't quite C++ anymore, while my metaphor is that it is clearly C++, just a slightly different dialect because programs utilize other forms, conventions, and written styles.
Here are my primary reasons for disabling them:
Binary Compatibility
Crossing language and translation boundaries is not universally well defined, or undefined. If you want to guarantee your program operates within the domain of defined behavior, you will need to quarantine exceptions at module exit points.
Executable Size
Here are the binary sizes of an exception free program I wrote, built without and with exceptions enabled:
Without exceptions:
- executable + dependencies: 330
- final stripped executable (release build): 37
With exceptions:
- executable + dependencies: 380
- final stripped executable (release build): 44
Reminder: That's a collection of libraries and programs which contain zero throws/catches. The compiler flag does enable exceptions in the C++ standard library. Therefore, the cost in the real world is more than 19% seen in this example.
Compiler: apple gcc4.2 + llvm. Sizes in MB.
Speed
Despite the term "zero cost exceptions", they still add some overhead even when nothing ever throws. In the above case, it is a performance critical program (Signal Processing, Generation, Presentation, Conversions, with large data sets/signals etc.). Exceptions are not a necessary feature in this design, while performance is very important.
Program Correctness
Seems like a strange reason... If throwing is not an option, you must write relatively strict, correct, well tested programs to guarantee your program executes correctly, and that clients use the interfaces correctly (if you give me a bad argument or do not check an error code, then you deserve UB). The result? Implementation quality improves greatly and problems get fixed quickly.
Simplicity
Exception handling implementations aren't often kept up to date. They also add a lot of complexity because an implementation can have many many many exit sequences. It's simpler to read and maintain highly complex programs when they use a small set of well defined, typed, exit strategies which bubble up to and are handled by the client. In other cases, the implementations may over time implement more throws or their dependencies may introduce them. Clients cannot easily or appropriately defend against all these exits. I write and update a lot of libraries, there is frequent evolution and improvement. Attempting to keep that all in synch with exception exit sequences (in a large codebase) would not be a good use of time, and would likely add a lot of noise and cruft. Due to increased program correctness and more tests, many (certainly not all) of the potential issues/exits can be ruled out.
History/Existing Code
In some cases, they were never introduced for historical reasons. An existing codebase did not use them, changing the programs could take man-years and make it really ugly to maintain because of overlap in conventions and implementations.
Downsides
Of course, there are downsides, the biggest are: Incompatability (incl. binary) with other libraries, and the fact that you will have to implement a good amount of programs to fit this model.