Catching thrown exceptions is quite different from passing arguments to functions.
There are similarities, but there are also subtle differences.
The 3 main differences are:
- exceptions are always copied at least once (not possible to avoid at all)
catch
clauses are examined in the order they are declared (not best-fit)
- they are subject to fewer forms of type conversions:
- inheritance-based coversions,
- conversion from a typed to an untyped pointer (
const void*
catches any pointer)
Any other kind of conversion is not allowed (e.g. int
to double
, or implicit const char*
to string
- your example).
Regarding your question in the comment
Suppose a hierarchy exists:
class Base {};
class Derived: public Base {};
class Base2 {};
class Leaf: public Derived, public Base2 {};
Now depending on the order of catch
clauses, an appropriate block will be executed.
try {
cout << "Trying ..." << endl;
throw Leaf();
} catch (Base& b) {
cout << "In Base&";
} catch (Base2& m) {
cout << "In Base2&"; //unreachable due to Base&
} catch (Derived& d) {
cout << "In Derived&"; // unreachable due to Base& and Base2&
}
If you switch Base
and Base2
catch order you will notice a different behavior.
If Leaf
inherited privately from Base2
, then catch Base2&
would be unreachable no matter where placed (assuming we throw a Leaf
)
Generally it's simple: order matters.
Derived()
is aBase
and can be bound by aBase&
, but"coercion"
is not astd::string
. The catch clauses capture the existing object. – TrapeziformmyOwnString
? Wouldn't that be ambiggy? – Gerrard