The exception declaration in a catch
clause looks like a function parameter list with exactly one parameter. As in a parameter list, we can omit the name of the catch parameter if the catch
has no need to access the thrown expression.
The type of the declaration determines what kinds of exceptions the handler can catch. The type must be a complete type (§ 7.3.3, p. 278). The type can be an lvalue reference but may not be an rvalue reference (§ 13.6.1, p. 532).
When a catch
is entered, the parameter in its exception declaration is initialized by the exception object. As with function parameters, if the catch
parameter has a nonreference type, then the parameter in the catch
is a copy of the exception object; changes made to the parameter inside the catch
are made to a local copy, not to the exception object itself. If the parameter has a reference type, then like any reference parameter, the catch
parameter is just another name for the exception object. Changes made to the parameter are made to the exception object.
Also like a function parameter, a catch
parameter that has a base-class type can be initialized by an exception object that has a type derived from the parameter type. If the catch
parameter has a nonreference type, then the exception object will be sliced down (§ 15.2.3, p. 603), just as it would be if such an object were passed to an ordinary function by value. On the other hand, if the parameter is a reference to a base-class type, then the parameter is bound to the exception object in the usual way.
Again, as with a function parameter, the static type of the exception declaration determines the actions that the catch
may perform. If the catch
parameter has a base-class type, then the catch
cannot use any members that are unique to the derived type.
Ordinarily, a catch
that takes an exception of a type related by inheritance ought to define its parameter as a reference.
During the search for a matching catch
, the catch
that is found is not necessarily the one that matches the exception best. Instead, the selected catch
is the first one that matches the exception at all. As a consequence, in a list of catch
clauses, the most specialized catch
must appear first.
Because catch
clauses are matched in the order in which they appear, programs that use exceptions from an inheritance hierarchy must order their catch
clauses so that handlers for a derived type occur before a catch
for its base type.
The rules for when an exception matches a catch
exception declaration are much more restrictive than the rules used for matching arguments with parameter types. Most conversions are not allowed—the types of the exception and the catch
declaration must match exactly with only a few possible differences:
• Conversions from nonconst
to const
are allowed. That is, a throw
of a nonconst
object can match a catch
specified to take a reference to const
.
• Conversions from derived type to base type are allowed.
• An array is converted to a pointer to the type of the array; a function is converted to the appropriate pointer to function type.
No other conversions are allowed to match a catch
. In particular, neither the standard arithmetic conversions nor conversions defined for class types are permitted.
Multiple catch
clauses with types related by inheritance must be ordered from most derived type to least derived.
Sometimes a single catch
cannot completely handle an exception. After some corrective actions, a catch
may decide that the exception must be handled by a function further up the call chain. A catch
passes its exception out to another catch
by rethrowing the exception. A rethrow is a throw
that is not followed by an expression:
throw;
An empty throw
can appear only in a catch
or in a function called (directly or indirectly) from a catch
. If an empty throw
is encountered when a handler is not active, terminate
is called.
A rethrow does not specify an expression; the (current) exception object is passed up the chain.
In general, a catch
might change the contents of its parameter. If, after changing its parameter, the catch
rethrows the exception, then those changes will be propagated only if the catch
’s exception declaration is a reference:
catch (my_error &eObj) { // specifier is a reference type
eObj.status = errCodes::severeErr; // modifies the exception object
throw; // the status member of the exception object is severeErr
} catch (other_error eObj) { // specifier is a nonreference type
eObj.status = errCodes::badErr; // modifies the local copy only
throw; // the status member of the exception object is unchanged
}
Sometimes we want to catch any exception that might occur, regardless of type. Catching every possible exception can be a problem: Sometimes we don’t know what types might be thrown. Even when we do know all the types, it may be tedious to provide a specific catch
clause for every possible exception. To catch all exceptions, we use an ellipsis for the exception declaration. Such handlers, sometimes known as catch-all handlers, have the form catch(...)
. A catch-all clause matches any type of exception.
A catch(...)
is often used in combination with a rethrow expression. The catch
does whatever local work can be done and then rethrows the exception:
void manip() {
try {
// actions that cause an exception to be thrown
}
catch (...) {
// work to partially handle the exception
throw;
}
}
A catch(...)
clause can be used by itself or as one of several catch
clauses.