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


2176. Destroying the returned object when a destructor throws

Section: 7.6.1.3  [expr.call]     Status: CD4     Submitter: Richard Smith     Date: 2015-09-28

[Adopted at the February, 2016 meeting.]

Consider the following example:

  #include <stdio.h>

  struct X {
    X() { puts("X()"); }
    X(const X&) { puts("X(const X&)"); }
    ~X() { puts("~X()"); }
  };

  struct Y { ~Y() noexcept(false) { throw 0; } };

  X f() {
    try {
      Y y;
      return {};
    } catch (...) {
    }
    return {};
  }

  int main() {
    f();
  }

Current implementations print X() twice but ~X() only once. That is obviously wrong, but it is not clear that the current wording covers this case.

Proposed resolution (February, 2016):

Change 14.3 [except.ctor] paragraph 2 as follows:

The destructor is invoked for each automatic object of class type constructed, but not yet destroyed, since the try block was entered. If an exception is thrown during the destruction of temporaries or local variables for a return statement (8.7.4 [stmt.return]), the destructor for the returned object (if any) is also invoked. The automatic objects are destroyed in the reverse order of the completion of their construction. [Example:

  struct A { };

  struct Y { ~Y() noexcept(false) { throw 0; } };

  A f() {
    try {
      A a;
      Y y;
      A b;
      return {};   // #1
    } catch (...) {
    }
    return {};     // #2
  }

At #1, the returned object of type A is constructed. Then, the local variable b is destroyed (8.7 [stmt.jump]). Next, the local variable y is destroyed, causing stack unwinding, resulting in the destruction of the returned object, followed by the destruction of the local variable a. Finally, the returned object is constructed again at #2. —end example]