> Can somebody please explain the difference between the below two approaches?
The exception declaration in a handler is similar to the parameter declaration of a unary function. The differences are: 1. r-value references are not permitted. 2. No conversions other than derived class to base class conversions and ignoring top-level cv-qualifiers are performed 3. Entry into handlers are attempted in order of appearance during the stack unwind - ie. there is no overload resolution.
1 2 3 4 5 6 7 8 9 10
void function( A ) ; // A is passed by value
void function( A& ) ; // A is passed by reference
void function( const A& ) ; // A is passed by reference to const
// ...
try { /* ... */ }
catch( A ) { /* ... */ } // A is caught by value
catch( A& ) { /* ... */ } // A is caught by reference
catch( const A& ) { /* ... */ } // A is caught by reference to const
> also any examples of where this might be useful?
The canonical exception declaration in a handler is of the form: catch( const T& r ).
This has several advantages: T can be an abstract interface; virtual functions invoked on r will behave polymorphically; and it avoids an operation that may fail (copy construction of the object) during stack unwind and the consequent call of unexpected()
#include <iostream>
#include <typeinfo>
struct base
{
base() {}
base( const base& ) { std::cout << "base::copy constructor\n" ; }
virtual ~base() { std::cout << "base::destructor\n" ; }
};
struct derived : virtual base
{
derived() { std::cout << "derived::constructor\n" ; }
~derived() { std::cout << "derived::destructor which invokes " ; }
} ;
void foo()
{
std::cout << "\n----------- foo -----------------\n" ;
try { throw derived() ; } // copy-elision is applied here
catch( base a ) // catches base by value
// the derived that was thrown is sliced to create a new object of type base
{
std::cout << "caught object of type: " << typeid(a).name() << '\n' ;
}
}
void bar()
{
std::cout << "\n----------- bar -----------------\n" ;
try { throw derived() ; } // copy-elision is applied here
catch( const base& a ) // catches base by reference to const
// what is caught is the derived that was thrown - no new object is created
{
std::cout << "caught object of type: " << typeid(a).name() << '\n' ;
}
}
int main()
{
foo() ;
bar() ;
}
Output:
----------- foo -----------------
derived::constructor
base::copy constructor
caught object of type: 4base
base::destructor
derived::destructor which invokes base::destructor
----------- bar -----------------
derived::constructor
caught object of type: 7derived
derived::destructor which invokes base::destructor