Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between throw and throw with arg of caught exception?

Tags:

Imagine two similar pieces of code:

try {   [...] } catch (myErr &err) {   err.append("More info added to error...");   throw err; } 

and

try {   [...] } catch (myErr &err) {   err.append("More info added to error...");   throw; } 

Are these effectively the same or do they differ in some subtle way? For example, does the first one cause a copy constructor to be run whereas perhaps the second reuses the same object to rethrow it?

like image 224
WilliamKF Avatar asked Sep 26 '09 16:09

WilliamKF


2 Answers

Depending on how you have arranged your exception hierarchy, re-throwing an exception by naming the exception variable in the throw statement may slice the original exception object.

A no-argument throw expression will throw the current exception object preserving its dynamic type, whereas a throw expression with an argument will throw a new exception based on the static type of the argument to throw.

E.g.

int main() {     try     {         try         {             throw Derived();         }         catch (Base& b)         {             std::cout << "Caught a reference to base\n";             b.print(std::cout);             throw b;         }     }     catch (Base& b)     {         std::cout << "Caught a reference to base\n";         b.print(std::cout);     }      return 0; } 

As written above, the program will output:

Caught a reference to base Derived Caught a reference to base Base

If the throw b is replace with a throw, then the outer catch will also catch the originally thrown Derived exception. This still holds if the inner class catches the Base exception by value instead of by reference - although naturally this would mean that the original exception object cannot be modified, so any changes to b would not be reflected in the Derived exception caught by the outer block.

like image 79
CB Bailey Avatar answered Sep 23 '22 08:09

CB Bailey


In the second case according to C++ Standard 15.1/6 copy constructor is not used:

A throw-expression with no operand rethrows the exception being handled. The exception is reactivated with the existing temporary; no new temporary exception object is created. The exception is no longer considered to be caught; therefore, the value of uncaught_exception() will again be true.

In the first case new exception will be thrown according to 15.1/3:

A throw-expression initializes a temporary object, called the exception object, the type of which is determined by removing any top-level cv-qualifiers from the static type of the operand of throw and adjusting the type from “array of T” or “function returning T” to “pointer to T” or “pointer to function returning T”, respectively. <...> The temporary is used to initialize the variable named in the matching handler (15.3). The type of the throw-expression shall not be an incomplete type, or a pointer or reference to an incomplete type, other than void*, const void*, volatile void*, or const volatile void*. Except for these restrictions and the restrictions on type matching mentioned in 15.3, the operand of throw is treated exactly as a function argument in a call (5.2.2) or the operand of a return statement.

In both cases copy constructor is required at throw stage (15.1/5):

When the thrown object is a class object, and the copy constructor used to initialize the temporary copy is not accessible, the program is ill-formed (even when the temporary object could otherwise be eliminated). Similarly, if the destructor for that object is not accessible, the program is ill-formed (even when the temporary object could otherwise be eliminated).

like image 24
Kirill V. Lyadvinsky Avatar answered Sep 23 '22 08:09

Kirill V. Lyadvinsky