Prev: ideas for data binding?
Next: Low memory conditions (was: Re: CoW and reference counting in the STL)
From: Daniel Krügler on 2 May 2010 22:01 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 8 May 2010 02:23 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 11 May 2010 17:07 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 13 May 2010 03:36 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 16 May 2010 08:46 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! ]
First
|
Prev
|
Next
|
Last
Pages: 1 2 3 Prev: ideas for data binding? Next: Low memory conditions (was: Re: CoW and reference counting in the STL) |