Consider the following program:
#include <iostream>
using namespace std;
class Base{};
class Derived: public Base {};
int main()
{
Derived d;
try{
throw d;
}
catch(Base b){
cout<<"Caught Base Exception";
}
catch(Derived d){
cout<<"Caught Derived exception";
}
return 0;
}
The output is going to be "Caught Base Exception".
As I udnerstand, catch blocks do not perform casting, and in order to use polymorphism we have to use deliver the thrown object by reference or as a pointer. So how come we do not witness slicing here? Why does the catch block recognise the thrown object as its base type when we did not delvier by reference/as a pointer?
Thanks in advance.
CodePudding user response:
So how come we do not witness slicing here?
You do observe slicing: b is a sliced-off base object from the original d, just like it would if you did Base b = d;.
As for why execution landed in the catch(Base): that's not because the derived type wasn't recognized, but because execution lands into the first compatible catch block, as Clang eloquently warns:
<source>:18:10: warning: exception of type 'Derived' will be caught by earlier handler [-Wexceptions]
Swapping the catches so that the Derived one has a chance to match outputs Caught Derived exception, as expected. Not that it would still be better to catch by reference, so as to not slice a potential child of Derived.
CodePudding user response:
It matches the first in the list. Base will match Derived first because it's in order. Even the compiler warns you.
Source>:18:10: warning: exception of type 'Derived' will be caught by earlier handler [-Wexceptions]
catch(Derived d){
^
<source>:15:10: note: for type 'Base'
catch(Base b){
^
1 warning generated.
Compiler returned: 0
If you switch the order, everything compiles fine and the output is the expected
#include <iostream>
using namespace std;
class Base{};
class Derived: public Base {};
int main()
{
Derived d;
try{
throw d;
}
catch(Derived d){
cout<<"Caught Derived exception";
}
catch(Base b){
cout<<"Caught Base Exception";
}
return 0;
}
Result
Program stdout
Caught Derived exception
Godbolt: https://godbolt.org/z/sxj11Wcoq
CodePudding user response:
The catch blocks are tested in the order you coded them. So, the Base catch will match the thrown d first (and yes, it does slice):
int main()
{
Derived d;
try{
throw d;
}
catch(Base b){ // <-- matches first, since d is derived from Base
cout<<"Caught Base Exception";
}
catch(Derived d){
cout<<"Caught Derived exception";
}
return 0;
}
But, if you change the order of the catch blocks, the Derived catch will match the thrown d first (and not slice):
int main()
{
Derived d;
try{
throw d;
}
catch(Derived d){ // <-- matches first, since d is a Derived
cout<<"Caught Derived exception";
}
catch(Base b){
cout<<"Caught Base Exception";
}
return 0;
}
As such, it is best to code your catch blocks in order from most specific type to most generic type, ie derived types before base types (especially for std exceptions), ... last, etc.
