Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is catching incomplete exception types by reference disallowed by the standard?

From C++17 standard (draft), 18.3.1:

[...] The exception-declaration shall not denote a pointer or reference to an incomplete type [...]

What is the reasoning behind disallowing catching incomplete types by reference?

If a function parameter is passed by reference, the complete type is only needed if the function actually accesses the object received, but it could just happily pass on the argument to another function without knowing anything about the type.

I cannot see why this should be different with exceptions - the exception data itself might reside anywhere appropriate, and on the (unwinded up to the handler) stack we find a reference to. Fine. If the handler now can drag sufficient information just from the type of the exception, why should it need to be aware of the complete definition?

So what did I miss?

like image 660
Aconcagua Avatar asked Jun 07 '18 07:06

Aconcagua


2 Answers

struct Alice;
struct Bob;

int main() {
    try {
        throwAlice(); // extern
    } catch (Bob&) {
        return 0;
    }
    return 1;
}

What does the following program returns? 0 or 1 or nasal demons? Well, it depends on Alice inheriting from Bob.

To handle the exception catching mechanism, the compiler must have that information at compile-time. Bob should be a complete type.

The reason why is explain in:

[except.handle]/15

The variable declared by the exception-declaration, of type cv T or cv T& [YSC: here, cv T& = Bob&], is initialized from the exception object, of type E [YSC: here, E = Alice], as follows:

  • if T is a base class of E, the variable is copy-initialized from the corresponding base class subobject of the exception object;
  • otherwise, the variable is copy-initialized from the exception object.

which confirms the compiler need to know if Bob inherits from Alice at compile-time: Bob must be a complete type.

like image 179
YSC Avatar answered Oct 19 '22 04:10

YSC


Based on VTT's comment to the question (unfortuntely deleted in the meanwhile, "exception handling relying on RTTI") and essential thoughts derived from YSC's answer:

Exceptions are thrown at run-time, the exception object is placed at some (in this scope unknwon, but) well defined location.

If now getting to catch the exception, we need to determine at run-time as well, if any of the exception handlers is a match for the exception currently thrown, i. e. some code like this:

try { throw e; }
catch(E1&) { /*...*/ }
catch(E2&) { /*...*/ }
catch(E3&) { /*...*/ }

Must result "somehwere" in code like this:

if(e instanceof(E1)) { /*...*/ }
else if(e instanceof(E2)) { /*...*/ }
else if(e instanceof(E3)) { /*...*/ }

with some appropriate instanceof definition (the term stolen from Java...). Such a definition now needs to compare if the RTTI of E<x> matches the RTTI of e or the one of any of e's base classes; naturally, for such comparison E<x>'s RTTI must be available at compile-time and thus the complete definition of E<x>.

Side note: If now RTTI contains base classes' RTTI in some kind of generic list, it is not even necessary any more to know at compile-time the exact type of the exception thrown.

like image 25
Aconcagua Avatar answered Oct 19 '22 04:10

Aconcagua