Alok
Alok

Reputation: 2035

If a thrown exception is always a copy of the exception object, why isn't this copy constructor being invoked?

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)?

Upvotes: 10

Views: 497

Answers (2)

Arthur Golubev 1985
Arthur Golubev 1985

Reputation: 677

Since C++11, when an object is thrown as an exception, the copy to exception object may be elided, and if the conditions for copy elision are met or would be met, except that the source is a function parameter, but a compiler is not eliding it for some reasons, the compiler must attempt to use the move constructor even if the object is designated by an lvalue.

Upvotes: 0

Lightness Races in Orbit
Lightness Races in Orbit

Reputation: 385098

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.
  • [..]

Upvotes: 11

Related Questions