Scott Meyers says:
C++ specifies that an object thrown as an exception is always copied and the copying is performed by the object's copy constructor.
But in my code:
struct test
{
test() { cout << "constructor is called" << endl; }
test(const test&) { cout << "copy constructor is called" << endl; }
~test() { cout << "destructor is called" << endl; }
};
void fun()
{
throw test();
}
int main()
{
try {
fun();
}
catch (test& t1) { cout << "exception handler" << endl; }
}
I do not see the exception object's copy constructor being called.
If I change the catch
to receive the exception object by value then it is, but according to Meyers's quote the exception object should have been copied even when it's received by reference.
Why is the copy constructor is not called (even when exception handling is performed by reference)?
Objects thrown as exceptions are always copied. But there may be cases where the default copy constructor doesn't work; specially when the class members are pointers. If we use such objects as exception objects we should make sure that our exception classes have proper copy constructors.
However, the copy constructor for an exception object still must not throw an exception because compilers are not required to elide the copy constructor call in all situations, and common implementations of std::exception_ptr will call a copy constructor even if it can be elided from a throw expression.
Meyers is correct that a copy is made, semantically:
[C++11: 12.2/1]:
Temporaries of class type are created in various contexts: binding a reference to a prvalue (8.5.3), returning a prvalue (6.6.3), a conversion that creates a prvalue (4.1, 5.2.9, 5.2.11, 5.4), throwing an exception (15.1), entering a handler (15.3), and in some initializations (8.5). [..]
[C++11: 15.1/4]:
The memory for the temporary copy of the exception being thrown is allocated in an unspecified way, except as noted in 3.7.3.1. The temporary persists as long as there is a handler being executed for that exception.
However, copies can be elided by clever compilers and they are allowed to do so regardless of side-effects.
[C++11: 12.8/31]:
When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the copy/move constructor and/or destructor for the object have side effects. In such cases, the implementation treats the source and target of the omitted copy/move operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects would have been destroyed without the optimization. This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):
- [..]
- when a temporary class object that has not been bound to a reference (12.2) would be copied/moved to a class object with the same cv-unqualified type, the copy/move operation can be omitted by constructing the temporary object directly into the target of the omitted copy/move.
- [..]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With