Home > Articles > Programming > C/C++

C++ Reference Guide

Hosted by

Function Try Blocks

Last updated Jan 1, 2003.

In 1995 the C++ standards committee extended the exception handling mechanism by adding a special type of handlers that may catch exceptions thrown during the construction of an object. This extension is known as function try blocks. Function try blocks differ from ordinary try and catch pairs in several aspects and they have very limited applications.

Ordinary Exception Handling versus Function Try Blocks

Recall that vanilla exception handling uses a try block, and one or more catch() blocks inside a function's body:

#include <string> 
#include <stdexcept> 
#include <iostream>
using namespace std;
void f () 
{ 
 bool trouble=true;
//...
 if (trouble)
  throw range_error(string("failure")); 
} 
int main ( ) 
{ 
 try  //inside a function's body
 { 
  f (); 
 } 
 catch (runtime_error& er) //inside a function's body
 { 
 cerr <<er.what(); 
 } 
 catch (...) //ditto
 { 
  cerr <<"unknown exception"; 
 } 
}//main()

99% or more of the exception handling code you've ever seen works like that. However, there are rare cases in which such traditional try and catch blocks won't do.

Take for example the following classes:

struct A
{
A(int n)
 {if (n>10) throw range_error("out of range");}
};
struct B: A
{
private:
string s;
//...
};

The constructor of B must use a member initialization list to pass arguments to the subobject s and A:

struct B: A
{
private:
 string s;
public:
B(int v, const string str) : 
 A(v), s(str) {}
};
int main()
{
B b(11, "test"); //construction will fail due to an exception
}

Here's the problem -- both A and s might throw during their construction. To handle such potential exceptions with the traditional exception handling mechanism you'd have to move the initialization of these subobjects inside the constructor body, and wrap these initializations with a try block. However, this is impossible because A doesn't have a default constructor and s requires an initializer. Therefore, you are forced to initialize the both of them in a member initialization list.

An exception thrown during construction is a special case because recovery isn't possible. If it were, the program would have an invalid object. That is why you never want to have exceptions thrown during construction, but if something of that sort does happen, you still don't want your program to leak resources.

Using A Function Try Block

So, how do you cope with the potential exceptions that A and s might throw during the construction of B? Function try blocks let you catch exceptions in unusual places, such as a constructor's member initialization list. In a function try block, the try keyword comes before the opening brace of a function's definition, and the catch handler comes after the function's closing brace, as in:

struct B: A
{
private:
 std::string s;
public:
B(int v, const string str) try : //notice where the try keyword appears
 A(v), s(str) {} //mem init list + body
catch(range_error &e) {} //handle A's exception
catch(bad_alloc &e) {} //handle string's exception
};//class B

The try keyword appears after the parameter list of the constructor, and before the colon indicating the member initialization list. Now, any exception thrown during the construction of B can be caught. As with regular exception handling, you may define multiple catch blocks to handle different types of exceptions.

No Recovery

In addition to the peculiar syntax, function try blocks differ from traditional exception handling in one crucial aspect -- you can't recover from an exception. This means that the catch blocks below the } of the constructor can either re-throw the exception they've caught (letting another handle intercept it), or throw another exception of a different type, say replacing bad_range and bad_alloc with a generic exception object. In either case, C++ will invoke std::terminate() to terminate the program.

As explained previously, the unconditional termination of the program ensures that the program shouldn't continue to run with invalid objects. Therefore, the catch blocks of a function try block should perform merely translate the exception and perhaps log it.

Not Just for Constructors

Although function try blocks are mostly useful in a constructor's member initialization list, as their name suggests, they may be used in theory at least with other types of functions -- destructors for example. I practice, however you never want to do that because throwing exceptions from a destructor is a very bad idea. It's therefore safe to restrict function try blocks to the rare occasion of exceptions that might occur inside a constructor's member initialization list.

In Conclusion

Throwing exception from a constructor is something you want to avoid. There are several techniques that can help you eliminate the risk of exceptions during construction. For instance, you may defer the initialization of members to a later stage (say by replacing member objects with pointers). If there's still a risk that a subobject might throw, a function try block will ensure that the exception is caught. However, do not attempt to recover from such an exception. Handlers of a constructor's function try block should only translate an exception into another exception type and perhaps log the error; they are not useful for any other purpose.