Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Re: Behavior of array deletion if an element's dtor throws

2 views
Skip to first unread message

cpp4ever

unread,
Apr 30, 2010, 1:19:36 PM4/30/10
to
On 04/30/2010 02:32 PM, Goran wrote:
> On Apr 30, 1:04 pm, Scott Meyers <NeverR...@aristeia.com> wrote:
>> I've asked a lot of questions about C++0x recently, so let me emphasize that I'm asking about current C++ (C++03) here.
>>
>> Consider:
>>
>> Widget *pwa = new Widget[100];
>> ...
>> delete [] pwa;
>>
>> Suppose that the destructor for pwa[50] throws. What happens next? 15.1
>> tells me that "control is transferred to the nearest handler with a
>> matching type," but 5.3.5/6 tells me that in a delete expression for an
>> array, "the elements will be destroyed in order of decreasing address (that
>> is, in reverse order of the completion of their constructor." So are
>> destructors invoked for array elements 0-49?
>>
>> My sense is that when pwa[50]'s destructor throws, the delete expression is
>> essentially abandoned (much as a looping expression would be abandoned if
>> an exception occurred during iteration), elements 0-49 of the array are not
>> destroyed, and the memory occupied by the array is not reclaimed by
>> operator delete (because that would have been performed by the
>> now-abandoned part of the delete expression), but I'd like to have more to
>> go on than how I sense things. So is the behavior of the above spelled out
>> by the standard?
>
> "Doctor, it hurts when I poke myself in the eye!" much?
>
> I don't know if it's spelled out in the standard, but I would be
> HUGELY surprised if any compiler's generated code would continue to
> call dtors etc after an exception was thrown. That would essentially
> mean that operator delete[] is (warning: compiled with head-compiler
> and tried with head-debugger):
>
> template<typename TYPE>
> void OperatorArrayDelete(TYPE* pBegin)
> {
> TYPE* pEnd = pBegin+ElementCount(pBegin);
> while (pEnd!= pBegin)
> try { (*--pEnd)->~Type(); } catch(...) {}
> // Well, that catch(...) {} smells to high heaven!
> SomehowFreeHeap(pBegin);
> }
>
> 15.1.must take precedence over 5.3.5/6. It must!!! Aaaaaarghhhhh! :-)
>
> Goran.
>
>
Goran has a damn fine point, depends where the exception is caught, and
what it then does. IMHO if you get an unexpected exception in a
destructor then something is potentially broken, and badly! Personally I
often spend more time on constructors and destructors than most other
member functions to avoid exceptions there. Getting it right is easier
than debugging, especially in those situations.

Regards

JB

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Kenneth 'Bessarion' Boyd

unread,
May 18, 2010, 11:55:41 AM5/18/10
to
On May 17, 1:22 pm, Scott Meyers <NeverR...@aristeia.com> wrote:

> There are two issues here. First, this issue doesn't change C++03, which is
> what we are discussing, although of course it can clarify its intent. More
> interestingly, the resolution to DR592 removes the text that states that
> new-expression-based array elements are destroyed, which makes the draft C++0x's
> 15.2/2 even weaker as regards the topic we are discussing. In draft C++0x (the
> FCD), 5.3.5/7 says that the deallocation function is called even if an exception
> is thrown,

In a note (e.g., this detail is intended to be derivable anyway even
if 5.3.5/7 is deleted from the FCD). I can't quickly derive this note
in terms of a "explicit requirements" reading of the standard, as
opposed to my preferred "maximal compliance" reading.

> something that is reiterated in 15.2/2. However, 5.3.5/6 is where
> the implicit loop destructing array elements is specified, yet this behavior is
> *not* reiterated in 15.2/2. This suggests to me that 5.3.5/6 is supposed to be
> abandoned in the event of an exception.

Yet abandoning the destructor calls for the remaining objects is
equivalent to their lifetime *not* ending, which contraindicates
calling the deallocation function.

Kenneth 'Bessarion' Boyd

unread,
May 18, 2010, 5:53:11 PM5/18/10
to
On May 18, 10:55 am, "Kenneth 'Bessarion' Boyd" <zaim...@zaimoni.com>
wrote:

> On May 17, 1:22 pm, Scott Meyers <NeverR...@aristeia.com> wrote:
>
> > There are two issues here. First, this issue doesn't change C++03, which is
> > what we are discussing, although of course it can clarify its intent. More
> > interestingly, the resolution to DR592 removes the text that states that
> > new-expression-based array elements are destroyed, which makes the draft C++0x's
> > 15.2/2 even weaker as regards the topic we are discussing. In draft C++0x (the
> > FCD), 5.3.5/7 says that the deallocation function is called even if an exception
> > is thrown,
>
> In a note (e.g., this detail is intended to be derivable anyway even
> if 5.3.5/7 is deleted from the FCD). I can't quickly derive this note
> in terms of a "explicit requirements" reading of the standard, as
> opposed to my preferred "maximal compliance" reading.

Insufficient proofreading: 15.2 is what allows 5.3.5/7 to exist as a
note.

Scott Meyers

unread,
Apr 30, 2010, 7:04:47 AM4/30/10
to
I've asked a lot of questions about C++0x recently, so let me emphasize that I'm asking about current C++ (C++03) here.

Consider:

Widget *pwa = new Widget[100];
...
delete [] pwa;

Suppose that the destructor for pwa[50] throws. What happens next? 15.1
tells me that "control is transferred to the nearest handler with a
matching type," but 5.3.5/6 tells me that in a delete expression for an
array, "the elements will be destroyed in order of decreasing address (that
is, in reverse order of the completion of their constructor." So are
destructors invoked for array elements 0-49?

My sense is that when pwa[50]'s destructor throws, the delete expression is
essentially abandoned (much as a looping expression would be abandoned if
an exception occurred during iteration), elements 0-49 of the array are not
destroyed, and the memory occupied by the array is not reclaimed by
operator delete (because that would have been performed by the
now-abandoned part of the delete expression), but I'd like to have more to
go on than how I sense things. So is the behavior of the above spelled out
by the standard?

Secondarily, is the behavior specified by C++0x any different?

Thanks,

Scott


--
* C++ and Beyond: Meyers, Sutter, & Alexandrescu, Oct. 24-27 near Seattle (http://cppandbeyond.com/)
* License my training materials for commercial (http://tinyurl.com/yfzvkp9) or personal use (http://tinyurl.com/yl5ka5p).

Goran

unread,
Apr 30, 2010, 9:32:11 AM4/30/10
to
On Apr 30, 1:04 pm, Scott Meyers <NeverR...@aristeia.com> wrote:
> I've asked a lot of questions about C++0x recently, so let me emphasize that I'm asking about current C++ (C++03) here.
>
> Consider:
>
> Widget *pwa = new Widget[100];
> ...
> delete [] pwa;
>
> Suppose that the destructor for pwa[50] throws. What happens next? 15.1
> tells me that "control is transferred to the nearest handler with a
> matching type," but 5.3.5/6 tells me that in a delete expression for an
> array, "the elements will be destroyed in order of decreasing address (that
> is, in reverse order of the completion of their constructor." So are
> destructors invoked for array elements 0-49?
>
> My sense is that when pwa[50]'s destructor throws, the delete expression is
> essentially abandoned (much as a looping expression would be abandoned if
> an exception occurred during iteration), elements 0-49 of the array are not
> destroyed, and the memory occupied by the array is not reclaimed by
> operator delete (because that would have been performed by the
> now-abandoned part of the delete expression), but I'd like to have more to
> go on than how I sense things. So is the behavior of the above spelled out
> by the standard?

"Doctor, it hurts when I poke myself in the eye!" much?

I don't know if it's spelled out in the standard, but I would be
HUGELY surprised if any compiler's generated code would continue to
call dtors etc after an exception was thrown. That would essentially
mean that operator delete[] is (warning: compiled with head-compiler
and tried with head-debugger):

template<typename TYPE>
void OperatorArrayDelete(TYPE* pBegin)
{
TYPE* pEnd = pBegin+ElementCount(pBegin);
while (pEnd!= pBegin)
try { (*--pEnd)->~Type(); } catch(...) {}
// Well, that catch(...) {} smells to high heaven!
SomehowFreeHeap(pBegin);
}

15.1.must take precedence over 5.3.5/6. It must!!! Aaaaaarghhhhh! :-)

Goran.


--

Nick Hounsome

unread,
Apr 30, 2010, 1:17:17 PM4/30/10
to
On 30 Apr, 12:04, Scott Meyers <NeverR...@aristeia.com> wrote:
> I've asked a lot of questions about C++0x recently, so let me emphasize that I'm asking about current C++ (C++03) here.
>
> Consider:
>
> Widget *pwa = new Widget[100];
> ...
> delete [] pwa;
>
> Suppose that the destructor for pwa[50] throws. What happens next? 15.1
> tells me that "control is transferred to the nearest handler with a
> matching type," but 5.3.5/6 tells me that in a delete expression for an
> array, "the elements will be destroyed in order of decreasing address (that
> is, in reverse order of the completion of their constructor." So are
> destructors invoked for array elements 0-49?
>
> My sense is that when pwa[50]'s destructor throws, the delete expression is
> essentially abandoned (much as a looping expression would be abandoned if
> an exception occurred during iteration), elements 0-49 of the array are not
> destroyed, and the memory occupied by the array is not reclaimed by
> operator delete (because that would have been performed by the
> now-abandoned part of the delete expression), but I'd like to have more to
> go on than how I sense things. So is the behavior of the above spelled out
> by the standard?
>
> Secondarily, is the behavior specified by C++0x any different?
>
> Thanks,
>
> Scott

I don't remember what it says in the standard but I did once read a
good description of why an implementation can never "do the the right
thing" in general which boils down to the question "What would you do
if 2 (or more) of the dtors threw? (elements 3 and 6 say)"
There is no way an implementation could throw/handle exceptions for
both so an implementation would either have to silently catch and
ignore all but the first (or last) exception or stop prematurely as
you suggest and the latter is clearly the least evil.

In view of the impossibility of getting anything right in the presence
of throwing dtors (It is well known that you can't gaurantee the post
conditions of many STL methods and hence the std provides specific
restrictive wording) It seems to me that the std ought to ban
exceptions from dtors everywhere explicitly - or at least call
std::unexpected/std::terminate but as far as I can see in N3000 it's
the same old stuff (including the bit about destruction in reverse
order)

A quick google shows that according to http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#219
it's still an open issue for C++0x which confuses me because I thought
that it was supoosed to be all done and dusted apart from dotting i's
and crossing t's.


--

Scott Meyers

unread,
Apr 30, 2010, 1:28:08 PM4/30/10
to
Goran wrote:
> "Doctor, it hurts when I poke myself in the eye!" much?

I actually meant to post this to comp.std.c++, where questions of the form "Doctor, is it supposed to hurt this much when I poke myself in the eye?" are not uncommon :-) (I've now reposted there.)

> I don't know if it's spelled out in the standard, but I would be
> HUGELY surprised if any compiler's generated code would continue to
> call dtors etc after an exception was thrown.

Somehow I think that "the language's behavior should not cause huge surprises" is not among its most influential design criteria. Heck, entire books have been written about the kinds of things people need to watch out for :-)

Scott

--
* C++ and Beyond: Meyers, Sutter, & Alexandrescu, Oct. 24-27 near Seattle (http://cppandbeyond.com/)
* License my training materials for commercial (http://tinyurl.com/yfzvkp9) or personal use (http://tinyurl.com/yl5ka5p).

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Goran

unread,
May 1, 2010, 4:26:56 PM5/1/10
to

OK, but there's nothing wrong in throwing from a constructor. In fact,
that's by far the best method of signaling construction errors. If you
are trying to avoid an exception from a constructor, you are most
likely using two-phase initialization almost all the time. And if you
do that, you are raising the danger of having objects that you can't
use (because e.g. a File object is useless if not "Open"; do you pre-
pend every read with "isOpen", or do you just cross fingers that you
did open it?).

Goran.

Goran

unread,
May 2, 2010, 8:49:42 AM5/2/10
to
On Apr 30, 7:28 pm, Scott Meyers <NeverR...@aristeia.com> wrote:
>> I don't know if it's spelled out in the standard, but I would be
>> HUGELY surprised if any compiler's generated code would continue to
>> call dtors etc after an exception was thrown.
>
> Somehow I think that "the language's behavior should not cause huge
> surprises" is not among its most influential design criteria.
> Heck, entire books have been written about the kinds of things people need
> to watch out for :-)

True, that. After years of C++-ing, I seldom wonder why language I
does what it does. But I indeed see a lot of people being amazed about
X or Y. I don't know if that's saying something good or bad about me.

Goran.

P.S. We need this Daniel guy now, he's the resident expert in closing
the discussion by quoting the standard around here.


--

Daniel Krügler

unread,
May 3, 2010, 9:01:40 AM5/3/10
to
On 30 Apr., 13:04, Scott Meyers <NeverR...@aristeia.com> wrote:
> I've asked a lot of questions about C++0x recently, so let me
> emphasize that I'm asking about current C++ (C++03) here.

[I already tried to reply on comp.std.c++ but
my message did not become available until now.
Since I'm off-line from tomorrow on for about
two weeks I try a second attempt here]

> Consider:
>
> Widget *pwa = new Widget[100];
> ...
> delete [] pwa;
>
> Suppose that the destructor for pwa[50] throws. What happens next? 15.1
> tells me that "control is transferred to the nearest handler with a
> matching type," but 5.3.5/6 tells me that in a delete expression for an
> array, "the elements will be destroyed in order of decreasing address (that
> is, in reverse order of the completion of their constructor." So are
> destructors invoked for array elements 0-49?

IMO the normative wording requires this and in fact, it requires
it for every array object, independent on the memory allocation
form. This can be concluded by combining the requirements
expressed in 5.3.5 [expr.delete]/6 that you already mentioned
and that of 15.2 [except.ctor]/2 of ISO/IEC 14882:2003(E):

"An object that is partially constructed [..] will have
destructors executed for all of its [..] subobjects for
which the constructor has completed execution and the
destructor has not yet begun execution.[..]"

which is also relevant for this case (but the wording
could be probably improved to make that clearer).

[My thanks to Mike Miller for reminding me of this one]

Let me add that this requirement does not invalidate
further requirements of the standard, e.g. assume
that more than one destructor of the array elements
throws. This would lead immediately to an invocation
of std::terminate, because it falls into
[except.terminate]/1 bullet 1.

> My sense is that when pwa[50]'s destructor throws, the delete expression is
> essentially abandoned (much as a looping expression would be abandoned if
> an exception occurred during iteration), elements 0-49 of the array are not
> destroyed, and the memory occupied by the array is not reclaimed by
> operator delete (because that would have been performed by the
> now-abandoned part of the delete expression),

This is incorrect, the deallocation is required to be called,
see 5.3.5 [expr.delete]/7:

"The delete-expression will call a deallocation
function (3.7.3.2)."

> but I'd like to have more to
> go on than how I sense things. So is the behavior of the above spelled out
> by the standard?
>
> Secondarily, is the behavior specified by C++0x any different?

No, it is not different, but the wording has been
improved a bit as part of resolving issue

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#353

to make especially 5.3.5/7 clearer by adding
the note:

"[ Note: The deallocation function is called regardless
of whether the destructor for the object or some
element of the array throws an exception. �end note ]"

HTH & Greetings from Bremen,

Daniel Kr�gler


--

A. McKenney

unread,
May 4, 2010, 3:44:04 AM5/4/10
to
On May 1, 4:26 pm, Goran <goran.pu...@gmail.com> wrote:
<snip>

> OK, but there's nothing wrong in throwing from a constructor. ...

To go off on a tangent, what's the case with
array constructors?

If you have

T tarray[100];

and the constructor for tarray[50] throws,
1. will t[51:99] _not_ be constructed?
2. will the destructors for tarray[0:49] be called?

It would make sense for this to happen, but,
as others have pointed out, C++ behavior isn't
always intuitively obvious. I'd feel better
if a C++ standards Guru explicitly said, "yes" to both.

(Actually, none of my C++ books is a good reference for the nooks
and crannies of exceptions and templates; I've mostly
had to learn by experiment. If we're lucky, maybe
Scott M. will come out with a "Nooks and Crannies
of C++" to complement his "Effective" series.
Maybe more like "Nooks and Crannies of C++, 2010",
"N&CoC++, 2011", etc.)

Scott Meyers

unread,
May 4, 2010, 3:59:32 AM5/4/10
to
Daniel Kr�gler wrote:
> [I already tried to reply on comp.std.c++ but
> my message did not become available until now.
> Since I'm off-line from tomorrow on for about
> two weeks I try a second attempt here]

I replied on csc (where your post has now appeared), and here's a copy of my reply:

Daniel Kr�gler wrote:


> On 30 Apr., 23:24, Scott Meyers <NeverR...@aristeia.com> wrote:
>> Suppose that the destructor for pwa[50] throws. What happens next? 15.1
>> tells me that "control is transferred to the nearest handler with a
>> matching type," but 5.3.5/6 tells me that in a delete expression for an
>> array, "the elements will be destroyed in order of decreasing address (that
>> is, in reverse order of the completion of their constructor." So are
>> destructors invoked for array elements 0-49?
>

> Yes, an implementation is required to destroy the remaining elements
> of the array, even if an exception occurs as of 5.3.5/6.

>> My sense is that when pwa[50]'s destructor throws, the delete expression is
>> essentially abandoned (much as a looping expression would be abandoned if
>> an exception occurred during iteration), elements 0-49 of the array are not
>> destroyed, and the memory occupied by the array is not reclaimed by
>> operator delete (because that would have been performed by the

>> now-abandoned part of the delete expression), but I'd like to have more to


>> go on than how I sense things. So is the behavior of the above spelled out
>> by the standard?
>

> The requirements mentioned in [expr.delete] and [except.ctor] are
> binding and an conforming implementation has to honor them. In
> regard to the call of the deallocation function, this is also
> required,
> see [expr.delete]/7:

But presumably 15.1 is binding, too. What in the standard says that the behavior dictated by 15.1 doesn't cause the behavior dictated by 5.3.5 to be abandoned? Or (equivalently), what in the standard says that the behavior dictated by 15.1 is deferred
until the behavior dictated by 5.3.5 has run to completion?

If 15.1 is deferred until 5.3.5 is done, why isn't 15.5.1 (invocation of terminate when an exception is thrown while another exception has been thrown but not caught) also deferred?

Informally (and I realize that using that word in this kind of a discussion in this newsgroup puts me on very shaky ground), we say that when an exception is thrown, normal execution stops, and control is transferred to a handler (if there is one). When
a throw expression executes, we stop executing looping constructs, jump out of blocks, etc. 3.5.3/6 is essentially dictating that compilers generate a looping construct. If we wrote the loop ourselves, we'd stop executing it and propagate an exception
(absent a catch block inside the loop). Where does the standard say that the compiler-generated loop doesn't do the same thing?

Scott


--
* C++ and Beyond: Meyers, Sutter, & Alexandrescu, Oct. 24-27 near Seattle (http://cppandbeyond.com/)
* License my training materials for commercial (http://tinyurl.com/yfzvkp9) or personal use (http://tinyurl.com/yl5ka5p).

Scott Meyers

unread,
May 4, 2010, 9:40:21 PM5/4/10
to
A. McKenney wrote:
> If you have
>
> T tarray[100];
>
> and the constructor for tarray[50] throws,
> 1. will t[51:99] _not_ be constructed?

Correct.

> 2. will the destructors for tarray[0:49] be called?

Ideally :-)

The implementation will start calling destructors for elements 0:49 (starting with 49), and all will be called, unless one of them throws. If that happens, you have two pending exceptions (the one for element 50 and the one for whatever element threw
while you were calling destructors), and in that case you call terminate right away.

Although I know that this is what happens, I can't actually find wording that spells it out in the standard. This has in fact become part of the question that started this thread (and which I believe is more property pursued in comp.std.c++, although so
far everything that has appeared there has also appeared here).

> (Actually, none of my C++ books is a good reference for the nooks
> and crannies of exceptions and templates; I've mostly
> had to learn by experiment.

Herb "Exceptional C++" Sutter has probably written more about the nooks and crannies of exceptions than anybody else. For template nooks and crannies, I'd first turn to "C++ Templates" by David Vandevoorde and Nicolai M. Josuttis. You'll run across some
discussions in my books, too, but not in as systematic a way as you'll find in these other authors' works. However, my books have nicer fonts, IMO :-)

Scott

--
* C++ and Beyond: Meyers, Sutter, & Alexandrescu, Oct. 24-27 near Seattle (http://cppandbeyond.com/)
* License my training materials for commercial (http://tinyurl.com/yfzvkp9) or personal use (http://tinyurl.com/yl5ka5p).

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Nikolay Ivchenkov

unread,
May 8, 2010, 1:23:05 PM5/8/10
to
On 30 Apr, 15:04, Scott Meyers <NeverR...@aristeia.com> wrote:
> I've asked a lot of questions about C++0x recently, so let me emphasize that I'm asking about current C++ (C++03) here.
>
> Consider:
>
> Widget *pwa = new Widget[100];
> ...
> delete [] pwa;
>
> Suppose that the destructor for pwa[50] throws. What happens next?

According to 15.2/2:
"An object that is partially constructed or partially destroyed will
have destructors executed for all of its fully constructed subobjects,
that is, for subobjects for which the constructor has completed


execution and the destructor has not yet begun execution."

The specification does not provide the definitions for "partially
constructed object" and "partially destroyed object" (IMO, they shall
be provided). Lets assume that "partially destroyed object" is defined
as follows:

An object is said to be partially destroyed when its destruction has
started and not completed. The destruction of a non-array object with
non-trivial destructor implies the execution of its destructor. The
destruction of an array object implies the destruction of all its
elements.

When the destructor for pwa[50] throws, the entire array is partially
destroyed by the delete-expression and, according to 15.2/2,
destructors shall be executed for all of its subobjects for which the
destructor has not yet begun execution. Note: VC++ 9.0 and Intel C++
11.0.061 follow these rules, but GNU C++ 4.5 don't.

However, I don't see any rule that would require the execution of the
deallocation function in this case. Note that the following rule is
irrelevant:

"If the object or array was allocated in a new-expression, the
matching deallocation function (3.7.3.2, 5.3.4, 12.5), if any, is
called to free the storage occupied by the object."

Widget *p = new Widget;
// the object was allocated in a new-expression;
// if the constructor throws, the deallocation function is called
// to free the storage occupied by the object

p->~Widget();
// if the destructor throws, the deallocation function is not
called
// to free the storage occupied by the object
new(p) Widget;

delete p;
// if the destructor throws, the deallocation function is not
called
// to free the storage occupied by the object

On 3 May, 17:01, Daniel Krugler <daniel.krueg...@googlemail.com>
wrote:


> On 30 Apr., 13:04, Scott Meyers <NeverR...@aristeia.com> wrote:
>

> > My sense is that when pwa[50]'s destructor throws, the delete expression is
> > essentially abandoned (much as a looping expression would be abandoned if
> > an exception occurred during iteration), elements 0-49 of the array are not
> > destroyed, and the memory occupied by the array is not reclaimed by
> > operator delete (because that would have been performed by the
> > now-abandoned part of the delete expression),
>

> This is incorrect, the deallocation is required to be called,
> see 5.3.5 [expr.delete]/7:
>
> "The delete-expression will call a deallocation
> function (3.7.3.2)."

Do you want to say that the deallocation function is required to be
called even if the destructor calls std::abort or std::exit?

#include <cstdio>
#include <cstdlib>

struct X
{
void operator delete[](void *p) throw()
{
std::printf("operator delete[]\n");
::operator delete[](p);
}
~X()
{
std::printf("~X()\n");
std::exit(0);
}
};

int main()
{
delete[] new X[2]; // ?
}

> > Secondarily, is the behavior specified by C++0x any different?
>

> No, it is not different, but the wording has been
> improved a bit as part of resolving issue
>
> http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#353
>
> to make especially 5.3.5/7 clearer by adding
> the note:
>
> "[ Note: The deallocation function is called regardless
> of whether the destructor for the object or some
> element of the array throws an exception. �end note ]"

This note violates ISO/IEC Directives, Part 2 - 6.5.1:

"Notes and examples integrated in the text of a document shall only be
used for giving additional information intended to assist the
understanding or use of the document. These elements shall not contain
requirements or any information considered indispensable for the use
of the document."

Unless otherwise specified, the execution of any subsequent evaluation
shall be abandoned if the current evaluation throws an exception or it
is the call to a noreturn function. Without this general implicit rule
the exception handling and noreturn functions would become almost
useless. The call to the deallocation function in a delete-expression
is a usual subsequent evaluation (I don't see any opposite statement
in C++03 or N3092).

VC++ 9.0 and Intel C++ 11.0.061 do not call the deallocation function
from a delete-expression if the object's destructor throws an
exception or calls std::exit or std::abort. If such a behavior is not
intended by the committee, this cannot be considered a compiler's bug
since there is no appropriate normative wording.


--

Scott Meyers

unread,
May 11, 2010, 10:22:54 AM5/11/10
to
Nikolay Ivchenkov wrote:
> On 30 Apr, 15:04, Scott Meyers <NeverR...@aristeia.com> wrote:
>> I've asked a lot of questions about C++0x recently, so let me emphasize that I'm asking about current C++ (C++03) here.
>>
>> Consider:
>>
>> Widget *pwa = new Widget[100];
>> ...
>> delete [] pwa;
>>
>> Suppose that the destructor for pwa[50] throws. What happens next?
>
> According to 15.2/2:
> "An object that is partially constructed or partially destroyed will
> have destructors executed for all of its fully constructed subobjects,
> that is, for subobjects for which the constructor has completed
> execution and the destructor has not yet begun execution."

[...]

> When the destructor for pwa[50] throws, the entire array is partially
> destroyed by the delete-expression and, according to 15.2/2,
> destructors shall be executed for all of its subobjects for which the
> destructor has not yet begun execution.

Except that 15.2/1 makes clear that section 15.2 pertains only to automatic
objects, and in the example I posted, we're not dealing with an automatic array
object. I thus conclude that 15.2/2 does not apply to the example I posted.

Even if it did, there would have to be a way to resolve the apparent
contradiction with 15.2/1, which says that throwing an exception transfers
control to a handler. This is in essence my original question, except my
concern was with the interaction of 5.3.5/6, which is to heap arrays what 15.2/2
is to automatic arrays.

Scott

--
* C++ and Beyond: Meyers, Sutter, & Alexandrescu, Oct. 24-27 near Seattle
(http://cppandbeyond.com/)
* License my training materials for commercial (http://tinyurl.com/yfzvkp9) or
personal use (http://tinyurl.com/yl5ka5p).

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Nikolay Ivchenkov

unread,
May 12, 2010, 4:07:28 AM5/12/10
to
On 11 May, 18:22, Scott Meyers <NeverR...@aristeia.com> wrote:
> Nikolay Ivchenkov wrote:
> > On 30 Apr, 15:04, Scott Meyers <NeverR...@aristeia.com> wrote:
> > > I've asked a lot of questions about C++0x recently, so let me
> > > emphasize that I'm asking about current C++ (C++03) here.
>
> > > Consider:
>
> > > Widget *pwa = new Widget[100];
> > > ...
> > > delete [] pwa;
>
> > > Suppose that the destructor for pwa[50] throws. What happens next?
>
> > According to 15.2/2:
> > "An object that is partially constructed or partially destroyed will
> > have destructors executed for all of its fully constructed subobjects,
> > that is, for subobjects for which the constructor has completed
> > execution and the destructor has not yet begun execution."
>
> [...]
>
> > When the destructor for pwa[50] throws, the entire array is partially
> > destroyed by the delete-expression and, according to 15.2/2,
> > destructors shall be executed for all of its subobjects for which the
> > destructor has not yet begun execution.
>
> Except that 15.2/1 makes clear that section 15.2 pertains only to automatic
> objects

I don't understand your conclusion. I think that 15.2/2 applies to
objects created by new-expressions as well. However, it is unclear
what should happen if the destructor called for a subobject of an
object with static storage duration throws:

#include <iostream>

struct A1
{
A1() { std::cout << "A1()" << std::endl; }
~A1() { std::cout << "~A1()" << std::endl; }
};

struct A2
{
A2() { std::cout << "A2()" << std::endl; }
~A2()
{
std::cout << "~A2()" << std::endl;
throw 0;
}
};

struct X
{
A1 a1;
A2 a2;
};

X x;
// shall x.a1 be destroyed before
// the program termination?

int main()
{
try
{
x.~X();
// shall C++03 - 15.5.1/1 bullet 4
// be applied here?
}
catch (int)
{
std::cout << "catched" << std::endl;
}

new (&x) X;
}

> Even if it did, there would have to be a way to resolve the apparent
> contradiction with 15.2/1, which says that throwing an exception transfers
> control to a handler.

I believe that the wording "When an exception is thrown, control is
transferred to the nearest handler with a matching type" is
incorrectly formulated since there are several cases when the control
never reaches any handler as a result of throwing an exception:
- when std::terminate is called under circumstances described in
15.5.1/1;
- when during stack unwinding any function calls std::abort or
std::exit;
- when during stack unwinding any function executes an infinite loop.

Actually, when an exception is thrown, the control may be immediately
transferred to:
- the copy constructor corresponding to the exception object, or
- the destructor for a recently constructed object, or
- the std::terminate function, or
- the std::unexpected function, or
- the nearest handler with a matching type.

Though 15.2 and 15.5 partially clarify the original intention, I think
that such style of description is definitely inappropriate for the
international standard. In other words, we have a fundamental defect
in the description of the exception handling mechanism. But I don't
think that the situation with array destruction is particularly
interesting in this respect.

--

Scott Meyers

unread,
May 12, 2010, 1:41:07 PM5/12/10
to
Nikolay Ivchenkov wrote:
> I don't understand your conclusion. I think that 15.2/2 applies to
> objects created by new-expressions as well. However, it is unclear
> what should happen if the destructor called for a subobject of an
> object with static storage duration throws:

I think both cases are equally unclear.

> Though 15.2 and 15.5 partially clarify the original intention, I think
> that such style of description is definitely inappropriate for the
> international standard. In other words, we have a fundamental defect
> in the description of the exception handling mechanism.

My primary conclusion based on the discussions of this topic here and in
comp.std.c++ is that the standard is at least unclear and arguably contradictory
as regards what should happen when, during destruction of an object, a
subobject's destructor throws. From what I can tell, C++0x does not improve the
situation much.

As you pointed out in your original post, gcc and MSVC offer different behaviors
when faced with my original example, and that means that this is no
theoretical problem, it's an issue in practice.

Scott

--
* C++ and Beyond: Meyers, Sutter, & Alexandrescu, Oct. 24-27 near Seattle
(http://cppandbeyond.com/)
* License my training materials for commercial (http://tinyurl.com/yfzvkp9) or
personal use (http://tinyurl.com/yl5ka5p).

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Nikolay Ivchenkov

unread,
May 13, 2010, 2:36:32 PM5/13/10
to
On 12 май, 21:41, Scott Meyers <NeverR...@aristeia.com> wrote:
> Nikolay Ivchenkov wrote:
> > I don't understand your conclusion. I think that 15.2/2 applies to
> > objects created by new-expressions as well. However, it is unclear
> > what should happen if the destructor called for a subobject of an
> > object with static storage duration throws:
>
> I think both cases are equally unclear.

What can you say about the destruction of a2 in the example below?

#include <iostream>

struct A
{
~A() { std::cout << "~A()\n"; }
};

struct X
{
X()
{
A a1; // is destroyed according to 15.2/1
throw 0;
}
A a2; // is destroyed according to 15.2/2
};

int main()
{
try
{
new X;
}
catch (int) {}
}

Also unclear?

> As you pointed out in your original post, gcc and MSVC offer different behaviors
> when faced with my original example, and that means that this is no
> theoretical problem, it's an issue in practice.

GNU C++ 4.5 supports 15.2/2 partially.

#include <iostream>

struct A
{
~A()
{
std::cout << "~A()" << std::endl;
throw 0;
}
};

struct X
{
A a1, a2;
};

int main()
{
std::cout << 1 << std::endl;
try
{
A a[2];
}
catch (int) {}

std::cout << 2 << std::endl;
try
{
X x;
}
catch (int) {}
}

It abandons the destruction in the former case and continues the
destruction in the latter case. I don't see any reasons for such a
difference.


--

Scott Meyers

unread,
May 15, 2010, 4:33:00 PM5/15/10
to
Nikolay Ivchenkov wrote:
> What can you say about the destruction of a2 in the example below?
>
> #include <iostream>
>
> struct A
> {
> ~A() { std::cout << "~A()\n"; }
> };
>
> struct X
> {
> X()
> {
> A a1; // is destroyed according to 15.2/1
> throw 0;
> }
> A a2; // is destroyed according to 15.2/2
> };
>
> int main()
> {
> try
> {
> new X;
> }
> catch (int) {}
> }
>
> Also unclear?

No, the behavior here seems to be well-specified in the way you have annotated.

> GNU C++ 4.5 supports 15.2/2 partially.
>
> #include <iostream>
>
> struct A
> {
> ~A()
> {
> std::cout << "~A()" << std::endl;
> throw 0;
> }
> };
>
> struct X
> {
> A a1, a2;
> };
>
> int main()
> {
> std::cout << 1 << std::endl;
> try
> {
> A a[2];
> }
> catch (int) {}
>
> std::cout << 2 << std::endl;
> try
> {
> X x;
> }
> catch (int) {}
> }
>
> It abandons the destruction in the former case and continues the
> destruction in the latter case. I don't see any reasons for such a
> difference.

Nor do I, actually. What strikes me now is the odd wording of 15.2/2:

> An object that is partially constructed or partially destroyed will have destructors executed for all of its
> fully constructed subobjects, that is, for subobjects for which the constructor has completed execution and

> the destructor has not yet begun execution. Should a constructor for an element of an automatic array
> throw an exception, only the constructed elements of that array will be destroyed. If the object or array was


> allocated in a new-expression, the matching deallocation function (3.7.3.2, 5.3.4, 12.5), if any, is called to
> free the storage occupied by the object.

Note that auto arrays are explicitly said to have their constructed elements destroyed, but this requirement is left out when heap arrays are mentioned. Perhaps that's supposed to be taken care of by 5.3.5/6, but I still think that 5.3.5/6 is
contradictory to 15.1/1. For more on this, see my May 5 posting to comp.std.c++. (I'd post a link, but currently, Google's newsgroup search capability is not working.)

Scott

--
* C++ and Beyond: Meyers, Sutter, & Alexandrescu, Oct. 24-27 near Seattle (http://cppandbeyond.com/)
* License my training materials for commercial (http://tinyurl.com/yfzvkp9) or personal use (http://tinyurl.com/yl5ka5p).

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Nikolay Ivchenkov

unread,
May 16, 2010, 7:46:23 PM5/16/10
to
On 16 May, 00:33, Scott Meyers <NeverR...@aristeia.com> wrote:
> Nikolay Ivchenkov wrote:
> > What can you say about the destruction of a2 in the example below?
>
> > #include <iostream>
>
> > struct A
> > {
> > ~A() { std::cout << "~A()\n"; }
> > };
>
> > struct X
> > {
> > X()
> > {
> > A a1; // is destroyed according to 15.2/1
> > throw 0;
> > }
> > A a2; // is destroyed according to 15.2/2
> > };
>
> > int main()
> > {
> > try
> > {
> > new X;
> > }
> > catch (int) {}
> > }
>
> > Also unclear?
>
> No, the behavior here seems to be well-specified in the way you have annotated.

15.2/2 covers the cases with partially destroyed objects (in
particular, during evaluation of a delete-expression) as well.

> What strikes me now is the odd wording of 15.2/2:
>
> > An object that is partially constructed or partially destroyed will have destructors executed for all of its
> > fully constructed subobjects, that is, for subobjects for which the constructor has completed execution and
> > the destructor has not yet begun execution. Should a constructor for an element of an automatic array
> > throw an exception, only the constructed elements of that array will be destroyed. If the object or array was
> > allocated in a new-expression, the matching deallocation function (3.7.3.2, 5.3.4, 12.5), if any, is called to
> > free the storage occupied by the object.
>
> Note that auto arrays are explicitly said to have their constructed elements destroyed, but this requirement is
> left out when heap arrays are mentioned.

Did you mean the sentence "Should a constructor for an element of an


automatic array throw an exception, only the constructed elements of

that array will be destroyed"? This is just redundant sentence, it was
removed as the resolution of the defect report 592:
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#592

The mention of "array" in the next sentence is also redundant since
arrays are objects.

> but I still think that 5.3.5/6 is contradictory to 15.1/1.

It's difficult to talk about contradiction when the specification has
so strange style of description. Apparently, the rules in the clause
15 are implicitly supposed to be dominating (but such an implicitness
is inappropriate for standards, IMO).

--

Scott Meyers

unread,
May 17, 2010, 2:22:02 PM5/17/10
to
Nikolay Ivchenkov wrote:
> 15.2/2 covers the cases with partially destroyed objects (in
> particular, during evaluation of a delete-expression) as well.

This is not obvious to me. 15.2/2 cannot stand on its own. The first sentence
makes no sense without some kind of context that indicates when it applies. The
obvious place to look for that context is 15.2/1, and 15.2/1 applies only to
automatic objects. The fact that 15.2/3 explicitly mentions automatic objects
reinforces the idea that 15.2 applies only to automatic objects, except where
otherwise explicitly mentioned (e.g., the final sentence of 15.2/2).

> Did you mean the sentence "Should a constructor for an element of an
> automatic array throw an exception, only the constructed elements of
> that array will be destroyed"? This is just redundant sentence, it was
> removed as the resolution of the defect report 592:
> http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#592

There are two issues here. First, this issue doesn't change C++03, which is

what we are discussing, although of course it can clarify its intent. More
interestingly, the resolution to DR592 removes the text that states that
new-expression-based array elements are destroyed, which makes the draft C++0x's
15.2/2 even weaker as regards the topic we are discussing. In draft C++0x (the
FCD), 5.3.5/7 says that the deallocation function is called even if an exception

is thrown, something that is reiterated in 15.2/2. However, 5.3.5/6 is where

the implicit loop destructing array elements is specified, yet this behavior is
*not* reiterated in 15.2/2. This suggests to me that 5.3.5/6 is supposed to be
abandoned in the event of an exception.

> It's difficult to talk about contradiction when the specification has


> so strange style of description. Apparently, the rules in the clause
> 15 are implicitly supposed to be dominating (but such an implicitness
> is inappropriate for standards, IMO).

I certainly agree that what's in C++03 and in draft C++0x is nowhere near as
clear as it should be.

Scott

--
* C++ and Beyond: Meyers, Sutter, & Alexandrescu, Oct. 24-27 near Seattle
(http://cppandbeyond.com/)
* License my training materials for commercial (http://tinyurl.com/yfzvkp9) or
personal use (http://tinyurl.com/yl5ka5p).

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Nikolay Ivchenkov

unread,
May 18, 2010, 11:48:51 AM5/18/10
to
On 17 May, 22:22, Scott Meyers <NeverR...@aristeia.com> wrote:
> 15.2/2 cannot stand on its own. The first sentence
> makes no sense without some kind of context that indicates when it applies.

I don't understand what do you mean.

> The
> obvious place to look for that context is 15.2/1, and 15.2/1 applies only to
> automatic objects. The fact that 15.2/3 explicitly mentions automatic objects
> reinforces the idea that 15.2 applies only to automatic objects, except where
> otherwise explicitly mentioned (e.g., the final sentence of 15.2/2).

15.2/2 begins with "An object", not with "An automatic object" nor
with "The object". So, I don't understand your reading.

> > Did you mean the sentence "Should a constructor for an element of an
> > automatic array throw an exception, only the constructed elements of
> > that array will be destroyed"? This is just redundant sentence, it was
> > removed as the resolution of the defect report 592:
> >http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#592
>
> There are two issues here. First, this issue doesn't change C++03, which is
> what we are discussing,

Redundant sentences do not cancel any other requirements.

> More
> interestingly, the resolution to DR592 removes the text that states that
> new-expression-based array elements are destroyed

I don't see this.

--

0 new messages