This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 113d. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.

2024-03-20


479. Copy elision in exception handling

Section: 14.2  [except.throw]     Status: CD1     Submitter: Mike Miller     Date: 07 Oct 2004

[Voted into WP at April, 2006 meeting.]

I have noticed a couple of confusing and overlapping passages dealing with copy elision. The first is 14.2 [except.throw] paragraph 5:

If the use of the temporary object can be eliminated without changing the meaning of the program except for the execution of constructors and destructors associated with the use of the temporary object (6.7.7 [class.temporary]), then the exception in the handler can be initialized directly with the argument of the throw expression.

The other is 14.4 [except.handle] paragraph 17:

If the use of a temporary object can be eliminated without changing the meaning of the program except for execution of constructors and destructors associated with the use of the temporary object, then the optional name can be bound directly to the temporary object specified in a throw-expression causing the handler to be executed.

I think these two passages are intended to describe the same optimization. However, as is often the case where something is described twice, there are significant differences. One is just different terminology — is “the exception in the handler” the same as “the object declared in the exception-declaration or, if the exception-declaration does not specify a name, a temporary object of that type” (14.4 [except.handle] paragraph 16)?

More significant, there is a difference in which kinds of throw-expressions are eligible for the optimization. In 14.2 [except.throw] paragraph 5, it appears that any object is a candidate, while in 14.4 [except.handle] paragraph 17 the thrown object must be a temporary (“the temporary object specified in a throw-expression”). For example, it's not clear looking at these two passages whether the copy of a local automatic can be elided. I.e., by analogy with the return value optimization described in 11.4.5.3 [class.copy.ctor] paragraph 15:

    X x;
    return x;    // copy may be elided

    X x;
    throw x;     // unclear whether copy may be elided

Which brings up another point: 11.4.5.3 [class.copy.ctor] paragraph 15 purports to be an exhaustive list in which copy elision is permitted even if the constructor and/or destructor have side effects; however, these two passages describe another case that is not mentioned in 11.4.5.3 [class.copy.ctor] paragraph 15.

A final point of confusion: in the unoptimized abstract machine, there are actually two copies in throwing and handling an exception: the copy from the object being thrown to the exception object, and the copy from the exception object to the object or temporary in the exception-declaration. 14.2 [except.throw] paragraph 5 speaks only of eliminating the exception object, copying the thrown object directly into the exception-declaration object, while 14.4 [except.handle] paragraph 17 refers to directly binding the exception-declaration object to the thrown object (if it's a temporary). Shouldn't these be separated, with a throw of an automatic object or temporary being like the return value optimization and the initialization of the object/temporary in the exception-declaration being a separate optimizable step (which could, presumably, be combined to effectively alias the exception-declaration onto the thrown object)?

(See paper J16/04-0165 = WG21 N1725.)

Proposed resolution (April, 2005):

  1. Add two items to the bulleted list in 11.4.5.3 [class.copy.ctor] paragraph 15 as follows:

    This elision of copy operations is permitted in the following circumstances (which may be combined to eliminate multiple copies):

    • in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object with the same cv-unqualified type as the function return type, the copy operation can be omitted by constructing the automatic object directly into the function's return value

    • in a throw-expression, when the operand is the name of a non-volatile automatic object, the copy operation from the operand to the exception object (14.2 [except.throw]) can be omitted by constructing the automatic object directly into the exception object

    • when a temporary class object that has not been bound to a reference (6.7.7 [class.temporary]) would be copied to a class object with the same cv-unqualified type, the copy operation can be omitted by constructing the temporary object directly into the target of the omitted copy

    • when the exception-declaration of an exception handler (Clause 14 [except]) declares an object of the same type (except for cv-qualification) as the exception object (14.2 [except.throw]), the copy operation can be omitted by treating the exception-declaration as an alias for the exception object if the meaning of the program will be unchanged except for the execution of constructors and destructors for the object declared by the exception-declaration

  2. Change 14.2 [except.throw] paragraph 5 as follows:

    If the use of the temporary object can be eliminated without changing the meaning of the program except for the execution of constructors and destructors associated with the use of the temporary object (6.7.7 [class.temporary]), then the exception in the handler can be initialized directly with the argument of the throw expression. When the thrown object is a class object, and the copy constructor used to initialize the temporary copy is not and the destructor shall be accessible, the program is ill-formed (even when the temporary object could otherwise be eliminated) even if the copy operation is elided (11.4.5.3 [class.copy.ctor]). Similarly, if the destructor for that object is not accessible, the program is ill-formed (even when the temporary object could otherwise be eliminated).
  3. Change 14.4 [except.handle] paragraph 17 as follows:

    If the use of a temporary object can be eliminated without changing the meaning of the program except for execution of constructors and destructors associated with the use of the temporary object, then the optional name can be bound directly to the temporary object specified in a throw-expression causing the handler to be executed. The copy constructor and destructor associated with the object shall be accessible even when the temporary object is eliminated if the copy operation is elided (11.4.5.3 [class.copy.ctor]).