Topic: Guaranteed order of destruction for auto objects?


Author: obi@finnbogi.ocs.com (Obi Thomas)
Date: Fri, 2 Apr 1993 21:06:03 GMT
Raw View
Does the standard guarantee the order of calling destructors for automatic
objects? Suppose I have a situation like this:

void FooBar()
{
    Foo a;
    Bar b(a);
    ...
}

"b" stores a reference to "a" in its constructor and then needs to use the
reference in its destructor. Is this safe? Am I guaranteed that "a" is
destroyed after "b" so that the reference to "a" is valid in Bar::~Bar()?

I couldn't find an answer on pp 575-6 of "The C++ Programming Language,"
2nd ed., but maybe I'm missing something?




Author: obi@finnbogi.ocs.com (Obi Thomas)
Date: Sat, 3 Apr 1993 17:52:59 GMT
Raw View
In article <C4vJy3.5FH@finnbogi.ocs.com>, I asked
> Does the standard guarantee the order of calling destructors for automatic
> objects?

I found the answer on page 171 (Section 5.5.1) of "The C++ Programming
Language, 2nd ed."

"Destructors for local variables are executed in reverse order of their
construction."




Author: bs@alice.att.com (Bjarne Stroustrup)
Date: 4 Apr 93 18:24:06 GMT
Raw View

Fobi@finnbogi.ocs.com (Obi Thomas @ Online Computer Systems, Inc.) writes

 > Does the standard guarantee the order of calling destructors for automatic
 > objects? Suppose I have a situation like this:
 >
 > void FooBar()
 > {
 >     Foo a;
 >     Bar b(a);
 >     ...
 > }
 >
 > "b" stores a reference to "a" in its constructor and then needs to use the
 > reference in its destructor. Is this safe? Am I guaranteed that "a" is
 > destroyed after "b" so that the reference to "a" is valid in Bar::~Bar()?

There isn't an official standard yet, of course, but `yes' you can rely on
the order of destruction of named local objects to be the reverse of their
order of construction.




Author: kanze@us-es.sel.de (James Kanze)
Date: 5 Apr 93 16:12:12
Raw View
In article <25207@alice.att.com> bs@alice.att.com (Bjarne Stroustrup)
writes:

|> Fobi@finnbogi.ocs.com (Obi Thomas @ Online Computer Systems, Inc.) writes

|>  > Does the standard guarantee the order of calling destructors for automatic
|>  > objects? Suppose I have a situation like this:
|>  >
|>  > void FooBar()
|>  > {
|>  >     Foo a;
|>  >     Bar b(a);
|>  >     ...
|>  > }
|>  >
|>  > "b" stores a reference to "a" in its constructor and then needs to use the
|>  > reference in its destructor. Is this safe? Am I guaranteed that "a" is
|>  > destroyed after "b" so that the reference to "a" is valid in Bar::~Bar()?

|> There isn't an official standard yet, of course, but `yes' you can rely on
|> the order of destruction of named local objects to be the reverse of their
|> order of construction.

Does this hold for the following?

 void
 FooBar()
 {
  goto A ;
 B :
  Foo  a ;
  goto C ;
 A :
  Bar  b ;
  goto B ;
 C :
  // ...
 }

(Note that b gets constructed before a.)

Or is this forbidden by the rule about jumping over an initializer?
--
James Kanze                             email: kanze@us-es.sel.de
GABI Software, Sarl., 8 rue du Faisan, F-67000 Strasbourg, France
Conseils en informatique industrielle --
                   -- Beratung in industrieller Datenverarbeitung




Author: bs@alice.att.com (Bjarne Stroustrup)
Date: 5 Apr 93 18:34:09 GMT
Raw View

kanze@us-es.sel.de (James Kanze @ SEL) writes

 > |>  but `yes' you can rely on
 > |> the order of destruction of named local objects to be the reverse of their
 > |> order of construction.
 >
 > Does this hold for the following?
 >
 >  void
 >  FooBar()
 >  {
 >   goto A ;
 >  B :
 >   Foo  a ;
 >   goto C ;
 >  A :
 >   Bar  b ;
 >   goto B ;
 >  C :
 >   // ...
 >  }
 >
 > (Note that b gets constructed before a.)
 >
 > Or is this forbidden by the rule about jumping over an initializer?

It is indeed not allowed to jump past an initializer (except of course if
the initializer is in an inner scope that is completely bypassed by the
jump).

This particular example could be allowed because there is no use of a or b
that can be reached without passing through their initialization, but I didn't
want to require that a compiler should be able to do full flow analysis.




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Tue, 6 Apr 1993 18:57:13 GMT
Raw View
> >  FooBar()
> >  {
> >   goto A ;
> >  B :
> >   Foo  a ;
> >   goto C ;
> >  A :
> >   Bar  b ;
> >   goto B ;
> >  C :
> >   // ...
> >  }
> >
> > (Note that b gets constructed before a.)
> >
> > Or is this forbidden by the rule about jumping over an initializer?
>
>It is indeed not allowed to jump past an initializer (except of course if
>the initializer is in an inner scope that is completely bypassed by the
>jump).
>

 Is there a rule about CALLING past an initialiser?

 This came up when I was considering nested functions.
 If there are forward declarations, it is possible
 for a nested function to reference an uninitialised variable.

 Well, while trying to work out sensible rules that
 would prevent this, it occured to me the problem
 ALREADY exists anyhow for non-nested functions:

 extern T g();

 T x=g(); // does a 'goto' that skips past an initialisation
 T y=k();
 T g() { return y;}

The second question is whether this rule (or no rule :-) is
acceptable for nested functions.


--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Tue, 6 Apr 1993 18:47:27 GMT
Raw View
In article <25220@alice.att.com> bs@alice.att.com (Bjarne Stroustrup) writes:
>
> >  FooBar()
> >  {
> >   goto A ;
> >  B :
> >   Foo  a ;
> >   goto C ;
> >  A :
> >   Bar  b ;
> >   goto B ;
> >  C :
> >   // ...
> >  }
> >
> > (Note that b gets constructed before a.)
> >
> > Or is this forbidden by the rule about jumping over an initializer?
>
>It is indeed not allowed to jump past an initializer (except of course if
>the initializer is in an inner scope that is completely bypassed by the
>jump).
>
>This particular example could be allowed because there is no use of a or b
>that can be reached without passing through their initialization, but I didn't
>want to require that a compiler should be able to do full flow analysis.

 Oh, but you would ALSO need to check the definition
of the constructors, in case they have important side effects,
that is, it would only make sense to allow the above code
if it didnt make any difference whether the construction
occured or not?


--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: kanze@us-es.sel.de (James Kanze)
Date: 7 Apr 93 17:58:26
Raw View
In article <1993Apr6.185713.18835@ucc.su.OZ.AU>
maxtal@physics.su.OZ.AU (John Max Skaller) writes:

|> > >  FooBar()
|> > >  {
|> > >   goto A ;
|> > >  B :
|> > >   Foo  a ;
|> > >   goto C ;
|> > >  A :
|> > >   Bar  b ;
|> > >   goto B ;
|> > >  C :
|> > >   // ...
|> > >  }
|> > >
|> > > (Note that b gets constructed before a.)
|> > >
|> > > Or is this forbidden by the rule about jumping over an initializer?
|> >
|> >It is indeed not allowed to jump past an initializer (except of course if
|> >the initializer is in an inner scope that is completely bypassed by the
|> >jump).
|> >

|>  Is there a rule about CALLING past an initialiser?

|>  This came up when I was considering nested functions.
|>  If there are forward declarations, it is possible
|>  for a nested function to reference an uninitialised variable.

What language are you programming in?  There are no nested functions
in C++.  (Note that even functions declared in a class declared within
a function cannot access the local variables of the containing
function.)

|>  Well, while trying to work out sensible rules that
|>  would prevent this, it occured to me the problem
|>  ALREADY exists anyhow for non-nested functions:

|>  extern T g();

|>  T x=g(); // does a 'goto' that skips past an initialisation
|>  T y=k();
|>  T g() { return y;}

|> The second question is whether this rule (or no rule :-) is
|> acceptable for nested functions.

This would seem to be just another variant of the initialization order
problem.  According to the ARM (and the present working draft of the
standard), this is legal, but the results are undefined.
--
James Kanze                             email: kanze@us-es.sel.de
GABI Software, Sarl., 8 rue du Faisan, F-67000 Strasbourg, France
Conseils en informatique industrielle --
                   -- Beratung in industrieller Datenverarbeitung




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Fri, 9 Apr 1993 15:31:46 GMT
Raw View
In article <KANZE.93Apr7175826@slsvdnt.us-es.sel.de> kanze@us-es.sel.de (James Kanze) writes:
>In article <1993Apr6.185713.18835@ucc.su.OZ.AU>
>maxtal@physics.su.OZ.AU (John Max Skaller) writes:
>
>|> > >  FooBar()
>|> > >  {
>|> > >   goto A ;
>|> > >  B :
>|> > >   Foo  a ;
>|> > >   goto C ;
>|> > >  A :
>|> > >   Bar  b ;
>|> > >   goto B ;
>|> > >  C :
>|> > >   // ...
>|> > >  }
>|> > >
>|> > > (Note that b gets constructed before a.)
>|> > >
>|> > > Or is this forbidden by the rule about jumping over an initializer?
>|> >
>|> >It is indeed not allowed to jump past an initializer (except of course if
>|> >the initializer is in an inner scope that is completely bypassed by the
>|> >jump).
>|> >
>
>|>  Is there a rule about CALLING past an initialiser?
>
>|>  This came up when I was considering nested functions.
>|>  If there are forward declarations, it is possible
>|>  for a nested function to reference an uninitialised variable.
>
>What language are you programming in?  There are no nested functions
>in C++.

 Not yet:-)

 But I'm working on a proposal for it.

 Thats why I asked this question.

 What I want to know is this: if I propose nested functions,
 what should be made of this:

 f() {
  auto g(); // forward declaration of nested function
  f() { g(); } // define f, it calls g
  f();      // call f, which calls g
  T x="hello";
  g() { cout <<x; } // g prints uninitialised rubbish
 };

 Should I work on banning this behaviour syntactically,
like jumping past an initialisation with goto,
or should it just be left specified as undefined behaviour,
as for the same example with global initialisation.

>
>|>  Well, while trying to work out sensible rules that
>|>  would prevent this, it occured to me the problem
>|>  ALREADY exists anyhow for non-nested functions:
>
>|>  extern T g();
>
>|>  T x=g(); // does a 'goto' that skips past an initialisation
>|>  T y=k();
>|>  T g() { return y;}
>
>|> The second question is whether this rule (or no rule :-) is
>|> acceptable for nested functions.
>
>This would seem to be just another variant of the initialization order
>problem.

 Yes, but its different in that, like the 'goto' problem,
the bodies of the functions involved are always visible,
or will soon be visible. For the initialisation order
problem, there might be a call to an external function,
so this problem is intractable.

 The nested function problem is solvable: the question
is whether the restrictions involved in solving it are
worth the effort.

>According to the ARM (and the present working draft of the
>standard), this is legal, but the results are undefined.


 Well, if I just propose nested functions, allowing
forward declarations, and not prohibiting such undefined
operations, when suitable restrictions might be crafted,
would yo vote in favour of the unrestricted proposal
or insist on examining a proposal with suitable restrictions?

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA