$ cat >test.cxx #include <cstdlib> struct a { ~a () { throw 1; } }; int f (a const& acr = a ()) { return 1; } struct b { b (int) { } ~b () { std::abort (); } }; int main () { try { b b_ (f ()); } catch (...) {} } $ g++ -v Reading specs from /home/boris/local/lib/gcc/x86_64-unknown-linux-gnu/3.4.0/specs Configured with: /home/boris/gcc-3.4.0/configure --prefix=/home/boris/local --enable-__cxa_atexit --enable-languages=c,c++ --enable-long-long --with-system-zlib --enable-shared --disable-multilib Thread model: posix gcc version 3.4.0 $ g++ test.cxx $ ./a.out I tried it on Intel C++ 8.0 and Compaq C++ 6.5-040 and they both call b::~b. -boris
b b_ (f ()); b is not initialized when ~a throws as it is equivant to int i; { const a &c= a(); i = f(c); } b b_ (i);
Andrew is right: b::b() isn't called (as you can easily verify by placing another call to std::abort into the constructor). Thus, there shouldn't be any call to the destructor either. W.
$ cat >test.cxx #include <iostream> using std::cerr; using std::endl; struct a { ~a () { throw 1; } }; int f (a const& acr = a ()) { return 1; } struct b { b (int) { cerr << "b" << endl; } ~b () { cerr << "~b" << endl; // std::abort (); } }; int main () { try { b b_ (f ()); } catch (...) {} } $ g++ test.cxx $ ./a.out b $ #?
Alright, I shouldn't first reduce and then draw conclusions. The call to f() is the necessary step. Here is a slightly smaller testcase: ------------------ extern "C" void abort (); int counter = 0; struct a { ~a () { throw 1; } }; int f (a const&) { return 1; } struct b { b (...) { ++counter; } ~b () { --counter; } }; int main () { try { b tmp(f (a())); } catch (...) {} if (counter != 0) abort (); } -------------- In b tmp(f(a())); the call to a::a() throws, so f return prematurely, and we shouldn't even start to construct tmp. However, we do, thus incrementing the counter. Then we fail to run the destructor, though. W.
Confirmed, indeed, with 2.95 through mainline.
> the call to a::a() throws ^^^^^^ a::~a()
Gee, yes, I can't seem to read code today. Since the lifetime of the temporary a() is until the end of the expression, the variable tmp is fully constructed. It needs to be destroyed again as part of the cleanup to be done when a::~a throws, but isn't. W.
The try/finally block for the tmp is not around the initializer, why?
This version of the test breaks under ICC: ---------------------- extern "C" void abort (); int counter = 0; int thrown = 0; struct a { ~a () { if (!thrown++) throw 1; } }; int f (a const&, const a&) { return 1; } struct b { b (...) { ++counter; } ~b () { --counter; } }; void g(); int main () { try { b tmp(f (a(), a())); } catch (...) {} if (counter != 0) abort (); } ---------------------- For whatever reason, if the first ~a throws, ICC doesn't try to destroy tmp, but if the second ~a throws, ICC cleans up tmp properly. Odd.
Subject: Bug 15764 Author: jason Date: Wed Oct 3 10:43:42 2007 New Revision: 128979 URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=128979 Log: PR c++/15764 * cp/decl.c (wrap_cleanups_r): New fn. (wrap_temporary_cleanups): New fn. (initialize_local_var): Call it. * tree-eh.c (same_handler_p): New fn. (optimize_double_finally): New fn. (refactor_eh_r): New fn. (refactor_eh): New fn. (pass_refactor_eh): New pass. * tree-pass.h: Declare it. * passes.c (init_optimization_passes): Add it. Added: trunk/gcc/testsuite/g++.dg/eh/init-temp1.C Modified: trunk/gcc/ChangeLog trunk/gcc/cp/ChangeLog trunk/gcc/cp/decl.c trunk/gcc/passes.c trunk/gcc/tree-eh.c trunk/gcc/tree-pass.h
Fixed for 4.3.