Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exception handling and coercion

try
{
    throw Derived();
}
catch (Base&)
{
    std::cout << "subtyping\n";
}

try
{
    throw "lol";
}
catch (std::string)
{
    std::cout << "coercion\n";
}

Output:

subtyping
terminate called after throwing an instance of 'char const*'

Why does exception handling play nice with subtyping, but not with coercion?

like image 778
fredoverflow Avatar asked Feb 26 '13 20:02

fredoverflow


2 Answers

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.

like image 108
emesx Avatar answered Oct 12 '22 19:10

emesx


Paragraph 15.3/3 of the C++11 Standard defines the exact conditions for a handler to be a match for a certain exception object, and these do not allow user-defined conversions:

A handler is a match for an exception object of type E if

— The handler is of type cv T or cv T& and E and T are the same type (ignoring the top-level cv-qualifiers), or

— the handler is of type cv T or cv T& and T is an unambiguous public base class of E, or

— the handler is of type cv1 T* cv2 and E is a pointer type that can be converted to the type of the handler by either or both of

  • a standard pointer conversion (4.10) not involving conversions to pointers to private or protected or ambiguous classes

  • a qualification conversion

— the handler is a pointer or pointer to member type and E is std::nullptr_t.

[ ... ]

like image 28
Andy Prowl Avatar answered Oct 12 '22 19:10

Andy Prowl