I read the following from C++ Primer (5th edition, Section 18.1.1): "When we throw an expression, the static, compile-time type of that expression determines the type of the exception object." So I tried the following code:
#include <iostream>
class Base{
public:
virtual void print(std::ostream& os){os << "Base\n";}
};
class Derived: public Base{
public:
void print(std::ostream& os){os << "Derived\n";}
};
int main(){
try{
Derived d;
Base &b = d;
b.print(std::cout); //line 1
throw b;
}
catch(Base& c){
c.print(std::cout); //line 2
}
return 0;
}
which gives me the following output:
Derived
Base
I think I understand why this output is expected: at line 1, we have dynamic binding. Now when we throw b, it is based on the static type of b, which means both the static type and the dynamic type of c is Base&, and therefore we see the result at line 2.
However, if I were to use a pointer, instead of a reference:
int main(){
try{
Derived d;
Base *b = &d;
b->print(std::cout); //line 1
throw b;
}
catch(Base* c){
c->print(std::cout); //line 2
}
return 0;
}
the output now becomes:
Derived
Derived
which seems to imply that the static type of c is Base*, but the dynamic type of c is Derived*, why? Shouldn't both the static and the dynamic types of c be Base*?
When we throw an expression, the static, compile-time type of that expression determines the type of the exception object
The above is entirely true. What you forget, is that pointers are objects too. And when you throw a pointer, that's your exception object.
The object you should have dynamically1 allocated is still pointed to by that Base*
pointer. And no slicing occurs on it, because there is no attempt to copy it. As such, dynamic dispatch via-pointer accesses a Derived
object, and that object will use the overriding function.
This "discrepancy" is why it is usually best to construct the exception object in the throw expression itself.
1 That pointer points to a local object, you did a big no no there and got yourself a dangling pointer.