C++ Catch Blocks - Catch Exception by Value or Reference

C++ catch blocks - catch exception by value or reference?

The standard practice for exceptions in C++ is ...

Throw by value, catch by reference

Catching by value is problematic in the face of inheritance hierarchies. Suppose for your example that there is another type MyException which inherits from CustomException and overrides items like an error code. If a MyException type was thrown your catch block would cause it to be converted to a CustomException instance which would cause the error code to change.

Is catching an exception by reference dangerous?

It is indeed safe - and recommended - to catch by const reference.

"e is actually placed on the stack of some_function()"

No it's not... the object actually thrown is created in an unspecified area of memory reserved for use by the exception handling mechanism:

[except.throw] 15.1/4: The memory for the exception object is allocated in an unspecified way, except as noted in 3.7.4.1. The exception
object is destroyed after either the last remaining active handler for the exception exits by any means other than rethrowing, or the last object of type std::exception_ptr (18.8.5) that refers to the exception object is destroyed, whichever is later.

If a local variable is specified to throw, it's copied there-to if necessary (the optimiser may be able to directly create it in this other memory). That's why...

15.1/5 When the thrown object is a class object, the constructor selected for the copy-initialization and the destructor
shall be accessible, even if the copy/move operation is elided (12.8).


If that's not clicked, it might help to imagine implementation vaguely like this:

// implementation support variable...
thread__local alignas(alignof(std::max_align_t))
char __exception_object[EXCEPTION_OBJECT_BUFFER_SIZE];

void some_function() {
// throw std::exception("some error message");

// IMPLEMENTATION PSEUDO-CODE:
auto&& thrown = std::exception("some error message");
// copy-initialise __exception_object...
new (&__exception_object) decltype(thrown){ thrown };
throw __type_of(thrown);
// as stack unwinds, _type_of value in register or another
// thread_local var...
}

int main(int argc, char **argv)
{
try {
some_function();
} // IMPLEMENTATION:
// if thrown __type_of for std::exception or derived...
catch (const std::exception& e) {
// IMPLEMENTATION:
// e references *(std::exception*)(&__exception_object[0]);
...
}
}

Is throw by value and catch by reference the law of thumb?

Why Stroustrup in his "A Tour of C++" catches by value?

Unless you want to copy the exception for some reason (e.g. store a custom exception type), you should always catch by const&. This might have been an oversight from Bjarne, or in that particular snippet it didn't really matter whether or not the exception was copied.

Related:

  • "Always catch exceptions by reference"

  • C++ catch blocks - catch exception by value or reference?


Why C++ is changing inconsistently? I mean from time to time everything changes. eg: in C++ 11:

There is no inconsistency here - static_assert merely became more flexible, as it doesn't force the user to provide an error message anymore. This is useful for cases where the boolean condition itself is self-explanatory and an error message would not add anything useful.

Exceptions C++: Catch by reference/value

The rule for catch clauses is that the first match is the one that's selected. Unlike overloaded functions, there is no notion of "best" match.

So in the example code, if stuff is false, the body for catch(derivedException) would be executed, because the thrown exception has type derivedException; if stuff is true, the body for catch(baseException) would be executed because the first catch clause doesn't match, but the second one does..

If the catch clauses were reversed, then regardless of the value of stuff, the body of the first catch clause (catch(baseException)) would be executed, because both exceptions can be caught by the base type.

Is there any reason to catch an exception by value?

The advantage to catching a reference, besides obvious reasons of not requiring copyability or performing a copy (potentially a slicing one), is that you can modify it and continue processing with throw;.

If you want to modify the object, yet continue exception processing with throw; on the non-modified object, then you must make a copy, and one way of doing so is catch by value.

I think it's pretty contrived, though. Catch by const reference followed by an explicit copy would better express intent.

Also note, throwing a new C++11 nested_exception will nest the previously thrown exception object, not the object received by value, within the new exception. In such conditions you could conceivably keep your own reference to the received exception, which would go stale unless you received it by reference.

Meaningless use of pass by reference syntax in catch block- exception?

You are catching the exception object by reference, but not throwing it as such.

Exceptions are always "thrown by value", because they must be allocated in a special region of your process's memory that is immune to the effects of stack unwinding.

[C++14: 15.1/3]: Throwing an exception copy-initializes (8.5, 12.8) a temporary object, called the exception object. The temporary is an lvalue and is used to initialize the variable declared in the matching handler (15.3). [..]

This is a general rule that is designed to account for the far more common case in which T is actually local to either the try block itself or its encapsulating function. It would be impossible to catch it from calling scopes if it were not copied.

We catch the exception object by reference so that you don't needlessly copy again the already-copied T. It also prevents slicing when your exceptions are in an inheritance heirarchy. Sometimes people use it to mutate the exception object before re-throwing it to calling scopes, though this appears to be a rarity.

Catching it by reference-to-const has the same benefit as catching any other thing by reference-to-const: it ensures that you do not mutate the exception. If you're not rethrowing it then there's no practical benefit here but if, like me, you write const by default as a fail-safe against mistakes, there's no reason not to use it.

do the default catch throw statements in C++ pass by value or reference

The catch-all catch (...) doesn't give you access to the exception object at all, so the question is moot. [Corrected:] Rethrowing with throw; throws the original object. If the handler catches by value, then changes to the local copy do not affect the original, rethrown object.[/] See 15.3 (esp. clause 17) for details.

Check out some of the related questions on the right, like this one or this one and this one and this one.

c++ exceptions throw by value catch by reference

First, when you write

throw Exception();

what's being thrown isn't actually the temporary object created by the prvalue expression Exception(). Conceptually, there's a separate object - the exception object - that's initialized from that temporary object, and it is the exception object that's actually thrown. (Compilers are allowed to elide the copy/move, though.)

Second, the language rules say that the exception object is always considered an lvalue. Hence it is allowed to bind to non-const lvalue references.

Why should i use catch by reference when exception pointers are thrown

The difference between the two codes you posted is that the first one catches a pointer to an exception by reference, and the second one catches a pointer to an exception by value. In neither case is an exception copied, since you're dealing with pointers.

In general, exceptions should be thrown by value, and caught by reference. The C++ standard library is designed with this expectation in mind. However, older libraries, (MFC for instance) throw exceptions by pointer as you do here, and are expected to be caught by pointer.

There's no effective difference between catching a pointer by value and by reference, except that if you catch by reference that gives you the (completely useless) option of deleting the exception, allocating a new exception with that same pointer, and rethrowing the same exception-pointer.



Related Topics



Leave a reply



Submit