Home > Software design >  Casting in a catch block (c throw and catch)
Casting in a catch block (c throw and catch)

Time:01-08

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;
}

Online Demo

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;
}

Online Demo

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.

  •  Tags:  
  • Related