If a base class constructor throws an exception while being called by derived class constructor in the initializer list and we might to handle that inside the derived class constructor, C++ provides function-try-block feature.

Better to understand this with the help of an example:

#include <iostream>
 
class Base
{
public:
    Base(int value)
    {
        throw 1;
    }
};
 
class Derived : public Base
{
public:
    Derived(int value)
    : Base{value}
    {
    }
};
 
int main(int argc, char const *argv[])
{
    try
    {
        Derived d{1};
    }
    catch (...)
    {
        std::cout << "Exception caught in main" << '\n';
    }
    return 0;
}

Here, Base constructor throws the exception when called in the initializer list of Derivedconstructor. The exception then gets caught in main function try-catch block.

If we want to handle that inside the Derived constructor, we can do something like below:

#include <iostream>
 
class Base
{
public:
    Base(int value)
    {
        throw 1;
    }
};
 
class Derived : public Base
{
public:
    Derived(int value)
    try
        : Base{value}
    {
    }
    catch (...)
    {
        std::cout << "Exception caught in derived constructor" << '\n';
        throw;
    }
};
 
int main(int argc, char const *argv[])
{
    try
    {
        Derived d{1};
    }
    catch (...)
    {
        std::cout << "Exception caught in main" << '\n';
    }
    return 0;
}

Here, we used function-try-block where we have written try after the Derived constructor parameter list. This makes the constructor body as try block. We then wrote catch block for the try block.

We have done this for the constructor but it is also possible to do it for destructor and normal functions. However, there few difference using function-try-block for each of these function types.

  1. When used with constructors, the catch can’t resolve the exception and it can’t have a return statement. catch block has to re-throw the same exception or a new exception. catch block implicitly does a same exception re-throw if not done explicitly.
  2. With destructors, the catch can resolve the exception by void return statement. Same as constructors, catch implicitly does a re-throw if reached the end of the block.
  3. When used with normal functions, catch can re-throw the exception or resolve using return statement. If function is a void and control reached at end of the function, exception is resolved implicitly. And if function is a non-void and there is no return statement at the function end, undefined behavior would occur.

Function-try-blocks are commonly used for handling exceptions in constructors.

Warning

Always avoid throwing exceptions from destructors because when the exception is thrown during the stack unwinding operation, the stack unwinding operation would break in between and data stored in stack would not get destroyed properly.

For example, a function’s local variables are stored inside the stack which can contain fundamental and program-defined types. When function an exception is occurred, these variables are destroyed during stack unwinding operation. In case of class, when stack unwinding happens, the class destructors are called. If any class destructors is in turn throwing another exception, the whole process of stack unwinding would get interrupted and other local variable of the function (or other functions) may not get destroyed properly.

References

  1. https://www.learncpp.com/cpp-tutorial/function-try-blocks/