[W]hy is the inheritance [w.r.t. exceptions] in the standard library not virtual?
Simply, multiple inheritance, in the standard exception hierarchy, wasn't intended to be supported. It is not virtually derived, and this is, in effect, what it means.
By contrast, where in the standard library is this supported? I/O streams is the first example that comes to mind. In particular the use of basic_ios
all the way down the hierarchy to basic_iostream
. In this case, it was intended that the base was virtually derived to support the multiple inheritance and that the "diamond problem" was avoided.
So why is this, how should std::exception
be used?
std::exception
has multiple exceptions that are derived from it, in particular, note the std::logic_error
and std::runtime_error
. The standard library has already given us a board pattern for classification and organisation of our exceptions, namely;
class logic_error;
Defines a type of object to be thrown as exception. It reports errors that are a consequence of faulty logic within the program such as violating logical preconditions or class invariants and may be preventable.
And
class runtime_error;
Defines a type of object to be thrown as exception. It reports errors that are due to events beyond the scope of the program and can not be easily predicted.
Of course these are not the only two, but they capture and are a base of a significant number of other standard library exceptions.
Where to root the exception hierarchy?
If you wish to use the standard library exception hierarchy, it is better to choose a point at which to extend the hierarchy and work from that point on. Hence, if there is a desire to have a custom root exception, then have std::exception
as a base class and derive further custom exceptions from that custom base onwards.
If the custom exceptions are divisible between runtime and logic errors, then derive the custom exception hierarchy from that level onwards.
Using a custom exception hierarchy rooted somewhere in the standard library exceptions is generally a good idea. At what point that root(s) should be is dependent on the actual intended use of the code. See here for a broader Q&A on this.
What about boost exceptions?
Boost uses virtual inheritance, they do this to exactly support the multiple inheritance that the standard library does not support. It also supports some additional features not found in the standard library.
That said, boost still uses the std::exception
as a base class.
Ultimately this becomes a design decision based on the inheritance structures you wish to support in the hierarchy.