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?
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
orcv 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 ofE
, 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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With