Rationale for throwing static type?
According to the C++ FAQ, when one throws an object, it's thrown using the static type of the expression. Hence, if you have:
catch ( some_exception const &e ) {
// ...
throw e; // throws static type, possibly causing "slicing"; should just "throw;" instead
}
and e
is actually a reference to some class derived from some_exception
, the above throw
will cause the object to be "sliced" silently. Yes开发者_开发问答, I know the correct answer is simply to throw;
, but the way things are seems like an unnecessary source of confusion and bugs.
What's the rationale for this? Why wouldn't you want it to throw by the dynamic type of the object?
When you throw
something, a temporary object is constructed from the operand of the throw
and that temporary object is the object that is caught.
C++ doesn't have built-in support for copying things or creating objects based on the dynamic type of an expression, so the temporary object is of the static type of the operand.
The "argument" to throw
is an expression and it is the type of the expression that determines the type of the exception object thrown. The type of the expression thrown doesn't necessarily have to be a polymorphic type so there may not be a way to determine if the expression actually refers to a base class subobject of a more derived type.
The simpler "type of the expression" rule also means that the implementation doesn't have to dynamically determine the size and type of the exception object at runtime which might require more complex and less efficient code to be generated for exception handling. If it had to do this it would represent the only place in a language where a copy constructor for a type unknown at the call point was required. This might add significantly to the cost of implementation.
Consider that we can have references to objects where the static type of the reference is copyable, but the dynamic type of the object is not.
struct foo {};
struct ncfoo : foo
{
private:
ncfoo(ncfoo const&) {}
};
ncfoo g_ncfoo;
void fun()
{
foo& ref = g_ncfoo;
throw ref; // what should be thrown here?
}
If you say "in this case just throw the static type", then how are the exact rules - what does "in this case" mean? References we just caught are "re-thrown" without copying, everything else is copied? Hm...
But however you define the rule, it would still be confusing. Throwing by-reference would lead to different behavior, depending on where we got that reference from. Neh. C++ is already complicated and confusing enough :)
精彩评论