The standard does not require special optimization in the case of an unnamed exception object. On the contrary, it then requires an effect as if a temporary is copy-initialized. This copying can result in memory being dynamically allocated.
N3290 §15.3/16:
If the exception-declaration specifies a name, it declares a variable which is copy-initialized (8.5) from the
exception object. If the exception-declaration denotes an object type but does not specify a name, a temporary (12.2) is copy-initialized (8.5) from the exception object. The lifetime of the variable or temporary
ends when the handler exits, after the destruction of any automatic objects initialized within the handler.
The paragraph above does not mention catching by reference, and so one might reasonably conclude that it applies whether or not the exception object is caught by reference; that a copy is constructed anyway.
However, that is contradicted by the next paragraph:
N3290 §15.3/17:
When the handler declares a non-constant object, any changes to that object will not affect the temporary
object that was initialized by execution of the throw-expression. When the handler declares a reference to
a non-constant object, any changes to the referenced object are changes to the temporary object initialized
when the throw-expression was executed and will have effect should that object be rethrown.
So, declared type T&
(with T
non-const
) is the single case where C++11 requires a reference directly to the thrown object, instead of copying. And it is also that way in C++03, except that C++03 has some additional wording about as-if optimization. So, for the formal the preference should be for
catch( T& name )
and
catch( T& )
However, I have always caught exceptions like catch( T const& )
. From a practical point of view one may assume that the compiler will optimize that to a direct reference to the thrown object, even though it is possible to devise cases where the observed program effect would then be non-standard. For example <evil grin>
#include <stdio.h>
#include <stdexcept>
struct Error
: std::runtime_error
{
public:
static Error* pThrown;
char const* pMessage_;
Error()
: std::runtime_error( "Base class message" )
, pMessage_( "Original message." )
{
printf( "Default-construction of Error object.\n" );
pThrown = this;
}
Error( Error const& other )
: std::runtime_error( other )
, pMessage_( other.pMessage_ )
{
printf( "Copy-construction of Error obejct.\n" );
}
char const* what() const throw() { return pMessage_; }
};
Error* Error::pThrown = 0;
int main()
{
printf( "Testing non-const ref:\n" );
try
{
throw Error();
}
catch( Error& x )
{
Error::pThrown->pMessage_ = "Modified message.";
printf( "%s\n", x.what() );
}
printf( "\n" );
printf( "Testing const ref:\n" );
try
{
throw Error();
}
catch( Error const& x )
{
Error::pThrown->pMessage_ = "Modified message";
printf( "%s\n", x.what() );
}
}
With both MinGW g++ 4.4.1 and Visual C++ 10.0 the output is …
Testing non-const ref:
Default-construction of Error object.
Modified message.
Testing const ref:
Default-construction of Error object.
Modified message
A pedantic formalist might say that both compilers are non-conforming, failing to create a copy for the Error const&
case. A purely practical practitioner might say that hey, what else did you expect? And me, I say that the wording in the standard is very far from perfect here, and that if anything, one should expect a clarification to explicitly allow the output above, so that also catching by reference to const
is both safe and maximally efficient.
Summing up wrt. the OP's question:
Catching by reference won’t call a copy constructor which could potentially terminate the program.
The standard only guarantees this for reference to non-const
.
In practice, as shown, it is also guaranteed for reference to const
, even when program results are then affected.
Cheers & hth.,