From: Daniel Krügler on
On 30 Apr., 13:04, Scott Meyers <NeverR...(a)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


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

From: Nikolay Ivchenkov on
On 30 Apr, 15:04, Scott Meyers <NeverR...(a)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...(a)googlemail.com>
wrote:
> On 30 Apr., 13:04, Scott Meyers <NeverR...(a)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.


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

From: Nikolay Ivchenkov on
On 11 May, 18:22, Scott Meyers <NeverR...(a)aristeia.com> wrote:
> Nikolay Ivchenkov wrote:
> > On 30 Apr, 15:04, Scott Meyers <NeverR...(a)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.

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

From: Nikolay Ivchenkov on
On 12 май, 21:41, Scott Meyers <NeverR...(a)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.


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

From: Nikolay Ivchenkov on
On 16 May, 00:33, Scott Meyers <NeverR...(a)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).

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