destructors moved out of place ?

Discussion in 'C++' started by vikimun@gmail.com, Mar 5, 2008.

  1. Guest

    I have a question whether compiler can move destructors past
    the place when it's normal out-of-scope place is. Is this true ?
    Example:
    I have a lock/unlock wrapped into the object SafeLock,

    where SafeLock::SafeLock() { m_lock.Lock(); }
    and SafeLock::~SafeLock() { m_lock.Unlock(); }
    void Class::Method(void) {
    SafeLock lock; // lock in ctor, unlocks in dtor
    ......
    // (1) lock is automatically unlocked here, ok.
    }

    So far, so good. The lock is unlocked at point (1), when it goes out
    of scope.

    Now let's look at the more complex case, with innner block:

    void Class::Method(void) {
    ......
    { // inner block
    SafeLock lock; // (4)
    } // (5)
    ... // (6)
    }

    Normally, lock(4) is destroyed at (5).

    But I was told this is not necessarily so;
    that C++ compiler is free to delay destruction of lock
    until later, until end of bigger bklock at (6).

    Is this true ? Is it indeed allowd ? I have difficulty to believe
    that
    standard allows this. Indeed, destructors can have side effects like
    closing files.

    Is it possible to have standard reference that explicitly
    prohibits this "optimization" ?

    Viki
     
    , Mar 5, 2008
    #1
    1. Advertising

  2. a écrit :
    > I have a question whether compiler can move destructors past
    > the place when it's normal out-of-scope place is. Is this true ?
    > Example:
    > I have a lock/unlock wrapped into the object SafeLock,
    >
    > where SafeLock::SafeLock() { m_lock.Lock(); }
    > and SafeLock::~SafeLock() { m_lock.Unlock(); }
    > void Class::Method(void) {
    > SafeLock lock; // lock in ctor, unlocks in dtor
    > ......
    > // (1) lock is automatically unlocked here, ok.
    > }
    >
    > So far, so good. The lock is unlocked at point (1), when it goes out
    > of scope.
    >
    > Now let's look at the more complex case, with innner block:
    >
    > void Class::Method(void) {
    > ......
    > { // inner block
    > SafeLock lock; // (4)
    > } // (5)
    > ... // (6)
    > }
    >
    > Normally, lock(4) is destroyed at (5).
    >
    > But I was told this is not necessarily so;
    > that C++ compiler is free to delay destruction of lock
    > until later, until end of bigger bklock at (6).
    >
    > Is this true ? Is it indeed allowd ? I have difficulty to believe
    > that
    > standard allows this. Indeed, destructors can have side effects like
    > closing files.


    The compiler could delay the destruction of the object if the overall
    behavior is the same (as you said, if there is no side effect).

    You are right, the lock is unlocked at (5).

    >
    > Is it possible to have standard reference that explicitly
    > prohibits this "optimization" ?


    The relevant parts are §12.4-10:
    Destructors are invoked implicitely [...] for a constructed object with
    automatic storage duration (3.7.2) when the block in which the object
    exits [...]

    And §3.7.2-3:
    If a named object has initialization or a destructor with side effects,
    it shall not be destroyed before the end of its block, nor shall it be
    eliminated as an optimization even if it appears to be unused, except
    that a class object or its copy may be eliminated as specified in 12.8.

    Michael
     
    Michael DOUBEZ, Mar 5, 2008
    #2
    1. Advertising

  3. Guest

    On Mar 5, 2:59 pm, Michael DOUBEZ <> wrote:
    > a écrit :
    >
    >
    >
    >
    >
    > > I have a question whether compiler can move destructors past
    > > the place when it's normal out-of-scope place is. Is this true ?
    > > Example:
    > > I have a lock/unlock wrapped into the object SafeLock,

    >
    > >      where   SafeLock::SafeLock() { m_lock.Lock(); }
    > >      and      SafeLock::~SafeLock() { m_lock.Unlock(); }
    > >      void Class::Method(void) {
    > >           SafeLock lock; // lock in ctor, unlocks in dtor
    > >           ......
    > >           // (1) lock is automatically unlocked here, ok.
    > >      }

    >
    > > So far, so good. The lock is unlocked at point (1), when it goes out
    > > of scope.

    >
    > > Now let's look at the more complex case, with innner block:

    >
    > >       void Class::Method(void) {
    > >          ......
    > >          { // inner block
    > >                 SafeLock lock; // (4)
    > >          } // (5)
    > >          ... // (6)
    > >       }

    >
    > > Normally, lock(4) is destroyed at (5).

    >
    > > But I was told this is not necessarily so;
    > > that C++ compiler is free to delay destruction of  lock
    > > until later, until end of bigger bklock at (6).

    >
    > > Is this true ? Is it indeed allowd ? I have difficulty to believe
    > > that
    > > standard allows this. Indeed, destructors can have side effects like
    > > closing files.

    >
    > The compiler could delay the destruction of the object if the overall
    > behavior is the same (as you said, if there is no side effect).
    >
    > You are right, the lock is unlocked at (5).
    >
    >
    >
    > > Is it possible to have standard reference that explicitly
    > > prohibits this "optimization" ?

    >
    > The relevant parts are §12.4-10:
    > Destructors are invoked implicitely [...] for a constructed object with
    > automatic storage duration (3.7.2) when the block in which the object
    > exits [...]
    >
    > And §3.7.2-3:
    > If a named object has initialization or a destructor with side effects,
    > it shall not be destroyed before the end of its block

    ^^^^^^
    Hmmm stange wording. It says "not before".
    "Shall not be destroyed before end of its block".
    But it does not say "not after".

    Shall we interpreted it as permission to
    invoke the destructor with side effects
    *after* end of the block ? What good would be this for ?

    I can see situations where this could be bad.

    Victoria

    > , nor shall it be
    > eliminated as an optimization even if it appears to be unused, except
    > that a class object or its copy may be eliminated as specified in 12.8.
    >
    > Michael- Hide quoted text -
    >
    > - Show quoted text -
     
    , Mar 5, 2008
    #3
  4. peter koch Guest

    On 5 Mar., 15:18, wrote:
    > On Mar 5, 2:59 pm, Michael DOUBEZ <> wrote:
    >
    >
    >
    > > a écrit :

    >
    > > > I have a question whether compiler can move destructors past
    > > > the place when it's normal out-of-scope place is. Is this true ?
    > > > Example:
    > > > I have a lock/unlock wrapped into the object SafeLock,

    >
    > > >      where   SafeLock::SafeLock() { m_lock.Lock(); }
    > > >      and      SafeLock::~SafeLock() { m_lock.Unlock(); }
    > > >      void Class::Method(void) {
    > > >           SafeLock lock; // lock in ctor, unlocks in dtor
    > > >           ......
    > > >           // (1) lock is automatically unlocked here, ok.
    > > >      }

    >
    > > > So far, so good. The lock is unlocked at point (1), when it goes out
    > > > of scope.

    >
    > > > Now let's look at the more complex case, with innner block:

    >
    > > >       void Class::Method(void) {
    > > >          ......
    > > >          { // inner block
    > > >                 SafeLock lock; // (4)
    > > >          } // (5)
    > > >          ... // (6)
    > > >       }

    >
    > > > Normally, lock(4) is destroyed at (5).

    >
    > > > But I was told this is not necessarily so;
    > > > that C++ compiler is free to delay destruction of  lock
    > > > until later, until end of bigger bklock at (6).

    >
    > > > Is this true ? Is it indeed allowd ? I have difficulty to believe
    > > > that
    > > > standard allows this. Indeed, destructors can have side effects like
    > > > closing files.

    >
    > > The compiler could delay the destruction of the object if the overall
    > > behavior is the same (as you said, if there is no side effect).

    >
    > > You are right, the lock is unlocked at (5).

    >
    > > > Is it possible to have standard reference that explicitly
    > > > prohibits this "optimization" ?

    >
    > > The relevant parts are §12.4-10:
    > > Destructors are invoked implicitely [...] for a constructed object with
    > > automatic storage duration (3.7.2) when the block in which the object
    > > exits [...]

    >
    > > And §3.7.2-3:
    > > If a named object has initialization or a destructor with side effects,
    > > it shall not be destroyed before the end of its block

    >
    >                             ^^^^^^
    > Hmmm stange wording. It says "not before".
    > "Shall not be destroyed before end of its block".
    > But it does not say "not after".


    Well, I believe the standard is quite clear - and all was quoted by
    Michael:

    > > Destructors are invoked implicitely [...] for a constructed object with
    > > automatic storage duration (3.7.2) when the block in which the object
    > > exits [...]


    and:

    > > If a named object has initialization or a destructor with side effects,
    > > it shall not be destroyed before the end of its block


    so it must not be destroyed before ever - not even if it looks as it
    is unused.

    So as Michael said, it is destroyed at exactly the point where the
    scope ends.


    So the destructor is implicitly invoked when the block exits.

    >
    > Shall we interpreted it as permission to
    > invoke the destructor with side effects
    > *after* end of the block ? What good would be this for ?
    >
    > I can see situations where this could be bad.
    >
    > Victoria
    >
    >
    >
    > > , nor shall it be
    > > eliminated as an optimization even if it appears to be unused, except
    > > that a class object or its copy may be eliminated as specified in 12.8.

    >
    > > Michael
     
    peter koch, Mar 5, 2008
    #4
  5. Guest

    On Mar 5, 4:48 pm, peter koch <> wrote:
    > On 5 Mar., 15:18, wrote:
    >
    >
    >
    >
    >
    > > On Mar 5, 2:59 pm, Michael DOUBEZ <> wrote:

    >
    > > > a écrit :

    >
    > > > > I have a question whether compiler can move destructors past
    > > > > the place when it's normal out-of-scope place is. Is this true ?
    > > > > Example:
    > > > > I have a lock/unlock wrapped into the object SafeLock,

    >
    > > > >      where   SafeLock::SafeLock() { m_lock.Lock(); }
    > > > >      and      SafeLock::~SafeLock() { m_lock.Unlock(); }
    > > > >      void Class::Method(void) {
    > > > >           SafeLock lock; // lock in ctor, unlocks in dtor
    > > > >           ......
    > > > >           // (1) lock is automatically unlocked here, ok.
    > > > >      }

    >
    > > > > So far, so good. The lock is unlocked at point (1), when it goes out
    > > > > of scope.

    >
    > > > > Now let's look at the more complex case, with innner block:

    >
    > > > >       void Class::Method(void) {
    > > > >          ......
    > > > >          { // inner block
    > > > >                 SafeLock lock; // (4)
    > > > >          } // (5)
    > > > >          ... // (6)
    > > > >       }

    >
    > > > > Normally, lock(4) is destroyed at (5).

    >
    > > > > But I was told this is not necessarily so;
    > > > > that C++ compiler is free to delay destruction of  lock
    > > > > until later, until end of bigger bklock at (6).

    >
    > > > > Is this true ? Is it indeed allowd ? I have difficulty to believe
    > > > > that
    > > > > standard allows this. Indeed, destructors can have side effects like
    > > > > closing files.

    >
    > > > The compiler could delay the destruction of the object if the overall
    > > > behavior is the same (as you said, if there is no side effect).

    >
    > > > You are right, the lock is unlocked at (5).

    >
    > > > > Is it possible to have standard reference that explicitly
    > > > > prohibits this "optimization" ?

    >
    > > > The relevant parts are §12.4-10:
    > > > Destructors are invoked implicitely [...] for a constructed object with
    > > > automatic storage duration (3.7.2) when the block in which the object
    > > > exits [...]

    >
    > > > And §3.7.2-3:
    > > > If a named object has initialization or a destructor with side effects,
    > > > it shall not be destroyed before the end of its block

    >
    > >                             ^^^^^^
    > > Hmmm stange wording. It says "not before".
    > > "Shall not be destroyed before end of its block".
    > > But it does not say "not after".

    >
    > Well, I believe the standard is quite clear - and all was quoted by
    > Michael:
    >
    > > > Destructors are invoked implicitely [...] for a constructed object with
    > > > automatic storage duration (3.7.2) when the block in which the object
    > > > exits [...]

    >
    > and:
    >
    > > > If a named object has initialization or a destructor with side effects,
    > > > it shall not be destroyed before the end of its block

    >
    > so it must not be destroyed before ever - not even if it looks as it
    > is unused.
    >
    > So as Michael said, it is destroyed at exactly the point where the
    > scope ends.
    >
    > So the destructor is implicitly invoked when the block exits.
    >
    >
    >
    >
    >
    > > Shall we interpreted it as permission to
    > > invoke the destructor with side effects
    > > *after* end of the block ? What good would be this for ?

    >
    > > I can see situations where this could be bad.

    >
    > > Victoria

    >
    > > > , nor shall it be
    > > > eliminated as an optimization even if it appears to be unused, except
    > > > that a class object or its copy may be eliminated as specified in 12.8..


    > So the destructor is implicitly invoked when the block exits.


    I do not see what you see. Optimizers are known to shuffle pieces of
    code around.

    People *observed* the destructors to be invoked at the end and
    *bigger* block
    because optimizer moved it and supposedly because standard does not
    disabllow it
    such optimizations *especially* for no-side-effects destructor.

    I do not mind optimizer shuffling around the no-side-effect
    destructors
    (provided that behaviour is preserved).

    For side-effects destructor, Michael showed that standard disallows
    moving the destructor invocation to *earlier* point.
    To me, that looks like permission for the optimizer to
    move destructor invocation to the later point, for side-effects dtors,
    and
    (2) to move the no-side-effects dtors invocation forward or backward
    (conditioned that it preserves results and behaviour, of course).

    I *do not* see where standard disallows the optimizer to move
    the side-effect destructor to later point. Where do you see this ?

    And given that people observed that optimizer moved destructor
    invocation
    to the end of *bigger* block, my question still remains, is this
    standard conformant, and does it make the diffence whether dtor has
    side effects or not wrt standard conformance ?

    Victoria
     
    , Mar 5, 2008
    #5
  6. a écrit :
    > On Mar 5, 4:48 pm, peter koch <> wrote:
    >> On 5 Mar., 15:18, wrote:
    >>
    >>
    >>
    >>
    >>
    >>> On Mar 5, 2:59 pm, Michael DOUBEZ <> wrote:
    >>>> a écrit :
    >>>>> I have a question whether compiler can move destructors past
    >>>>> the place when it's normal out-of-scope place is. Is this true ?
    >>>>> Example:
    >>>>> I have a lock/unlock wrapped into the object SafeLock,
    >>>>> where SafeLock::SafeLock() { m_lock.Lock(); }
    >>>>> and SafeLock::~SafeLock() { m_lock.Unlock(); }
    >>>>> void Class::Method(void) {
    >>>>> SafeLock lock; // lock in ctor, unlocks in dtor
    >>>>> ......
    >>>>> // (1) lock is automatically unlocked here, ok.
    >>>>> }
    >>>>> So far, so good. The lock is unlocked at point (1), when it goes out
    >>>>> of scope.
    >>>>> Now let's look at the more complex case, with innner block:
    >>>>> void Class::Method(void) {
    >>>>> ......
    >>>>> { // inner block
    >>>>> SafeLock lock; // (4)
    >>>>> } // (5)
    >>>>> ... // (6)
    >>>>> }
    >>>>> Normally, lock(4) is destroyed at (5).
    >>>>> But I was told this is not necessarily so;
    >>>>> that C++ compiler is free to delay destruction of lock
    >>>>> until later, until end of bigger bklock at (6).
    >>>>> Is this true ? Is it indeed allowd ? I have difficulty to believe
    >>>>> that
    >>>>> standard allows this. Indeed, destructors can have side effects like
    >>>>> closing files.
    >>>> The compiler could delay the destruction of the object if the overall
    >>>> behavior is the same (as you said, if there is no side effect).
    >>>> You are right, the lock is unlocked at (5).
    >>>>> Is it possible to have standard reference that explicitly
    >>>>> prohibits this "optimization" ?
    >>>> The relevant parts are §12.4-10:
    >>>> Destructors are invoked implicitely [...] for a constructed object with
    >>>> automatic storage duration (3.7.2) when the block in which the object
    >>>> exits [...]
    >>>> And §3.7.2-3:
    >>>> If a named object has initialization or a destructor with side effects,
    >>>> it shall not be destroyed before the end of its block
    >>> ^^^^^^
    >>> Hmmm stange wording. It says "not before".
    >>> "Shall not be destroyed before end of its block".
    >>> But it does not say "not after".

    >> Well, I believe the standard is quite clear - and all was quoted by
    >> Michael:
    >>
    >>>> Destructors are invoked implicitely [...] for a constructed object with
    >>>> automatic storage duration (3.7.2) when the block in which the object
    >>>> exits [...]

    >> and:
    >>
    >>>> If a named object has initialization or a destructor with side effects,
    >>>> it shall not be destroyed before the end of its block

    >> so it must not be destroyed before ever - not even if it looks as it
    >> is unused.
    >>
    >> So as Michael said, it is destroyed at exactly the point where the
    >> scope ends.
    >>
    >> So the destructor is implicitly invoked when the block exits.
    >>
    >>
    >>
    >>
    >>
    >>> Shall we interpreted it as permission to
    >>> invoke the destructor with side effects
    >>> *after* end of the block ? What good would be this for ?
    >>> I can see situations where this could be bad.
    >>> Victoria
    >>>> , nor shall it be
    >>>> eliminated as an optimization even if it appears to be unused, except
    >>>> that a class object or its copy may be eliminated as specified in 12.8.

    >
    >> So the destructor is implicitly invoked when the block exits.

    >
    > I do not see what you see. Optimizers are known to shuffle pieces of
    > code around.
    >
    > People *observed* the destructors to be invoked at the end and
    > *bigger* block
    > because optimizer moved it and supposedly because standard does not
    > disabllow it
    > such optimizations *especially* for no-side-effects destructor.
    >
    > I do not mind optimizer shuffling around the no-side-effect
    > destructors
    > (provided that behaviour is preserved).
    >
    > For side-effects destructor, Michael showed that standard disallows
    > moving the destructor invocation to *earlier* point.
    > To me, that looks like permission for the optimizer to
    > move destructor invocation to the later point, for side-effects dtors,
    > and
    > (2) to move the no-side-effects dtors invocation forward or backward
    > (conditioned that it preserves results and behaviour, of course).
    >
    > I *do not* see where standard disallows the optimizer to move
    > the side-effect destructor to later point. Where do you see this ?
    >
    > And given that people observed that optimizer moved destructor
    > invocation
    > to the end of *bigger* block, my question still remains, is this
    > standard conformant, and does it make the diffence whether dtor has
    > side effects or not wrt standard conformance ?


    Look a bit deeper at §6.6-2:
    On exit from a scope, destructor are called for all constructed objects
    with automatic storage duration (3.7.2)(named objects or temporaries)
    that are declared in that scope, in the reverse order of their
    declaration. [...]

    I don't see that the standard forbid such practice but doing so would in
    fact modify the observable order of execution (in the case of classes
    with side effects).
    How did these people *observe* this displacement ? Was it with a side
    effect call (cout) ?

    Michael
     
    Michael DOUBEZ, Mar 5, 2008
    #6
  7. Guest

    On Mar 5, 6:00 pm, Michael DOUBEZ <> wrote:
    > a écrit :
    >
    >
    >
    >
    >
    > > On Mar 5, 4:48 pm, peter koch <> wrote:
    > >> On 5 Mar., 15:18, wrote:

    >
    > >>> On Mar 5, 2:59 pm, Michael DOUBEZ <> wrote:
    > >>>> a écrit :
    > >>>>> I have a question whether compiler can move destructors past
    > >>>>> the place when it's normal out-of-scope place is. Is this true ?
    > >>>>> Example:
    > >>>>> I have a lock/unlock wrapped into the object SafeLock,
    > >>>>>      where   SafeLock::SafeLock() { m_lock.Lock(); }
    > >>>>>      and      SafeLock::~SafeLock() { m_lock.Unlock(); }
    > >>>>>      void Class::Method(void) {
    > >>>>>           SafeLock lock; // lock in ctor, unlocks in dtor
    > >>>>>           ......
    > >>>>>           // (1) lock is automatically unlocked here, ok.
    > >>>>>      }
    > >>>>> So far, so good. The lock is unlocked at point (1), when it goes out
    > >>>>> of scope.
    > >>>>> Now let's look at the more complex case, with innner block:
    > >>>>>       void Class::Method(void) {
    > >>>>>          ......
    > >>>>>          { // inner block
    > >>>>>                 SafeLock lock; // (4)
    > >>>>>          } // (5)
    > >>>>>          ... // (6)
    > >>>>>       }
    > >>>>> Normally, lock(4) is destroyed at (5).
    > >>>>> But I was told this is not necessarily so;
    > >>>>> that C++ compiler is free to delay destruction of  lock
    > >>>>> until later, until end of bigger bklock at (6).
    > >>>>> Is this true ? Is it indeed allowd ? I have difficulty to believe
    > >>>>> that
    > >>>>> standard allows this. Indeed, destructors can have side effects like
    > >>>>> closing files.
    > >>>> The compiler could delay the destruction of the object if the overall
    > >>>> behavior is the same (as you said, if there is no side effect).
    > >>>> You are right, the lock is unlocked at (5).
    > >>>>> Is it possible to have standard reference that explicitly
    > >>>>> prohibits this "optimization" ?
    > >>>> The relevant parts are §12.4-10:
    > >>>> Destructors are invoked implicitely [...] for a constructed object with
    > >>>> automatic storage duration (3.7.2) when the block in which the object
    > >>>> exits [...]
    > >>>> And §3.7.2-3:
    > >>>> If a named object has initialization or a destructor with side effects,
    > >>>> it shall not be destroyed before the end of its block
    > >>>                             ^^^^^^
    > >>> Hmmm stange wording. It says "not before".
    > >>> "Shall not be destroyed before end of its block".
    > >>> But it does not say "not after".
    > >> Well, I believe the standard is quite clear - and all was quoted by
    > >> Michael:

    >
    > >>>> Destructors are invoked implicitely [...] for a constructed object with
    > >>>> automatic storage duration (3.7.2) when the block in which the object
    > >>>> exits [...]
    > >> and:

    >
    > >>>> If a named object has initialization or a destructor with side effects,
    > >>>> it shall not be destroyed before the end of its block
    > >> so it must not be destroyed before ever - not even if it looks as it
    > >> is unused.

    >
    > >> So as Michael said, it is destroyed at exactly the point where the
    > >> scope ends.

    >
    > >> So the destructor is implicitly invoked when the block exits.

    >
    > >>> Shall we interpreted it as permission to
    > >>> invoke the destructor with side effects
    > >>> *after* end of the block ? What good would be this for ?
    > >>> I can see situations where this could be bad.
    > >>> Victoria
    > >>>> , nor shall it be
    > >>>> eliminated as an optimization even if it appears to be unused, except
    > >>>> that a class object or its copy may be eliminated as specified in 12.8.

    >
    > >> So the destructor is implicitly invoked when the block exits.

    >
    > > I do not see what you see. Optimizers are known to shuffle pieces of
    > > code around.

    >
    > > People *observed* the destructors to be invoked at the end and
    > > *bigger* block
    > > because optimizer moved it and supposedly because standard does not
    > > disabllow it
    > > such optimizations *especially* for no-side-effects destructor.

    >
    > > I do not mind optimizer shuffling around the no-side-effect
    > > destructors
    > > (provided that behaviour is preserved).

    >
    > > For side-effects destructor, Michael showed that standard disallows
    > > moving the destructor invocation to *earlier* point.
    > > To me, that looks like permission for the optimizer to
    > > move destructor invocation to the later point, for side-effects dtors,
    > > and
    > > (2) to move the no-side-effects dtors invocation forward or backward
    > > (conditioned that it preserves results and behaviour, of course).

    >
    > > I *do not* see where standard disallows the optimizer to move
    > > the side-effect destructor to later point. Where do you see this ?

    >
    > > And given that people observed that optimizer moved destructor
    > > invocation
    > > to the end of *bigger* block, my question still remains, is this
    > > standard conformant, and does it make the diffence whether dtor has
    > > side effects or not wrt standard conformance ?

    >
    > Look a bit deeper at §6.6-2:
    > On exit from a scope, destructor are called for all constructed objects
    > with automatic storage duration (3.7.2)(named objects or temporaries)
    > that are declared in that scope, in the reverse order of their
    > declaration. [...]
    >
    > I don't see that the standard forbid such practice but doing so would in
    > fact modify the observable order of execution (in the case of classes
    > with side effects).
    > How did these people *observe* this displacement ?

    With debugger, tracing and breakpoints.

    > Was it with a side effect call (cout) ?

    It had no printouts. I do not know whether it had other side-effects.
    With debugger, you can observe invocations whether they have or do not
    have
    side-effects.

    Victoria
     
    , Mar 5, 2008
    #7
  8. Micah Cowan Guest

    writes:

    > On Mar 5, 6:00 pm, Michael DOUBEZ <> wrote:
    >> How did these people *observe* this displacement ?

    > With debugger, tracing and breakpoints.
    >
    >> Was it with a side effect call (cout) ?

    > It had no printouts. I do not know whether it had other side-effects.
    > With debugger, you can observe invocations whether they have or do not
    > have
    > side-effects.


    Yes, but as Michael pointed out in his original response, whether or
    not they have side-effects changes what is required by the Standard.

    In your original example, the destructor releases a lock, which is
    presumably an observable side-effect¹, and so a C++ implementation
    absolutely _must_ call the destructor at the exact point of exit from
    its declarative block².

    (Very boring caveats which almost certainly do not apply to your case,
    but which I'm required to mention:) :)

    (1) Technically, only reads/writes to volatile data, modifications of
    objects, and calls to library I/O functions, as defined by the
    standard, are required to be "side effects". Since C++ doesn't define
    locking mechanisms, it's implementation-defined whether invoking such a
    mechanism constitutes a "side effect", though the C++ standard
    strongly suggests that non-standard I/O facilities be such. Be that as
    it may, if an implementation actually existed that did _not_ consider
    such a thing to be a side effect, nobody would use it, so you have
    little need to worry. :)

    (2) ...at least, as far as the observable effects of the "abstract
    machine" are concerned. No guarantees are made about how it looks
    under a debugger, but if the destructor weren't literally called at
    that exact point (extremely unlikely), it would have to be done in
    such a way that a conforming program could not tell that it hadn't
    been.

    --
    Micah J. Cowan
    Programmer, musician, typesetting enthusiast, gamer...
    http://micah.cowan.name/
     
    Micah Cowan, Mar 5, 2008
    #8
  9. peter koch Guest

    On 5 Mar., 16:13, wrote:
    > On Mar 5, 4:48 pm, peter koch <> wrote:
    >
    >
    >
    >
    >
    > > On 5 Mar., 15:18, wrote:

    >
    > > > On Mar 5, 2:59 pm, Michael DOUBEZ <> wrote:

    >
    > > > > a écrit :

    >
    > > > > > I have a question whether compiler can move destructors past
    > > > > > the place when it's normal out-of-scope place is. Is this true ?
    > > > > > Example:
    > > > > > I have a lock/unlock wrapped into the object SafeLock,

    >
    > > > > >      where   SafeLock::SafeLock() { m_lock.Lock(); }
    > > > > >      and      SafeLock::~SafeLock() { m_lock.Unlock(); }
    > > > > >      void Class::Method(void) {
    > > > > >           SafeLock lock; // lock in ctor, unlocks in dtor
    > > > > >           ......
    > > > > >           // (1) lock is automatically unlocked here, ok..
    > > > > >      }

    >
    > > > > > So far, so good. The lock is unlocked at point (1), when it goes out
    > > > > > of scope.

    >
    > > > > > Now let's look at the more complex case, with innner block:

    >
    > > > > >       void Class::Method(void) {
    > > > > >          ......
    > > > > >          { // inner block
    > > > > >                 SafeLock lock; // (4)
    > > > > >          } // (5)
    > > > > >          ... // (6)
    > > > > >       }

    >
    > > > > > Normally, lock(4) is destroyed at (5).

    >
    > > > > > But I was told this is not necessarily so;
    > > > > > that C++ compiler is free to delay destruction of  lock
    > > > > > until later, until end of bigger bklock at (6).

    >
    > > > > > Is this true ? Is it indeed allowd ? I have difficulty to believe
    > > > > > that
    > > > > > standard allows this. Indeed, destructors can have side effects like
    > > > > > closing files.

    >
    > > > > The compiler could delay the destruction of the object if the overall
    > > > > behavior is the same (as you said, if there is no side effect).

    >
    > > > > You are right, the lock is unlocked at (5).

    >
    > > > > > Is it possible to have standard reference that explicitly
    > > > > > prohibits this "optimization" ?

    >
    > > > > The relevant parts are §12.4-10:
    > > > > Destructors are invoked implicitely [...] for a constructed object with
    > > > > automatic storage duration (3.7.2) when the block in which the object
    > > > > exits [...]

    >
    > > > > And §3.7.2-3:
    > > > > If a named object has initialization or a destructor with side effects,
    > > > > it shall not be destroyed before the end of its block

    >
    > > >                             ^^^^^^
    > > > Hmmm stange wording. It says "not before".
    > > > "Shall not be destroyed before end of its block".
    > > > But it does not say "not after".

    >
    > > Well, I believe the standard is quite clear - and all was quoted by
    > > Michael:

    >
    > > > > Destructors are invoked implicitely [...] for a constructed object with
    > > > > automatic storage duration (3.7.2) when the block in which the object
    > > > > exits [...]

    >
    > > and:

    >
    > > > > If a named object has initialization or a destructor with side effects,
    > > > > it shall not be destroyed before the end of its block

    >
    > > so it must not be destroyed before ever - not even if it looks as it
    > > is unused.

    >
    > > So as Michael said, it is destroyed at exactly the point where the
    > > scope ends.

    >
    > > So the destructor is implicitly invoked when the block exits.

    >
    > > > Shall we interpreted it as permission to
    > > > invoke the destructor with side effects
    > > > *after* end of the block ? What good would be this for ?

    >
    > > > I can see situations where this could be bad.

    >
    > > > Victoria

    >
    > > > > , nor shall it be
    > > > > eliminated as an optimization even if it appears to be unused, except
    > > > > that a class object or its copy may be eliminated as specified in 12..8.

    > > So the destructor is implicitly invoked when the block exits.

    >
    > I do not see what you see. Optimizers are known to shuffle pieces of
    > code around.
    >
    > People *observed* the destructors to be invoked at the end and
    > *bigger* block
    > because optimizer moved it and supposedly because standard does not
    > disabllow it
    > such optimizations *especially* for no-side-effects destructor.
    >
    > I do not mind optimizer shuffling around the no-side-effect
    > destructors
    > (provided that behaviour is preserved).
    >
    > For side-effects destructor, Michael showed that standard disallows
    > moving the destructor invocation to *earlier* point.
    > To me, that looks like permission for the optimizer to
    > move destructor invocation to the later point, for side-effects dtors,
    > and
    > (2) to move the no-side-effects dtors invocation forward or backward
    > (conditioned that it preserves results and behaviour, of course).
    >
    > I *do not* see where standard disallows the optimizer to move
    > the side-effect destructor to later point. Where do you see this ?


    The way I read the "Destructors are invoked implicitely [...] for a
    constructed object with
    automatic storage duration (3.7.2) when the block in which the object
    exits " is quite clear. "when" indicates a point in time and can not
    be later.

    >
    > And given that people observed that optimizer moved destructor
    > invocation
    > to the end of *bigger* block, my question still remains, is this
    > standard conformant, and does it make the diffence whether dtor has
    > side effects or not wrt standard conformance ?


    If there was observable behaviour, the compiler would have a bug.
    There is absolutely no doubt about this, and I frankly doubt that this
    has been observed with any newer compiler. The idiom shown is so
    common that tons of code would break if standard behaviour was not
    followed. I am one of the many using the idiom shown as in your
    example - with locks. And I have NEVER had an indication that that
    should not work. It has never failed, and I've never seen an assembly
    listing where the above did not hold. But I do vaguely remember that
    VC 6.0 had a bug that prevented us from using it with all
    optimisations on (dont remember if the above was one of the problems).
    If you use that compiler it is just possible that you need to be
    careful.

    /Peter
     
    peter koch, Mar 5, 2008
    #9
  10. James Kanze Guest

    On Mar 5, 4:13 pm, wrote:
    > On Mar 5, 4:48 pm, peter koch <> wrote:
    > > On 5 Mar., 15:18, wrote:
    > > > On Mar 5, 2:59 pm, Michael DOUBEZ <> wrote:
    > > > > a écrit :
    > > > > > I have a question whether compiler can move destructors past
    > > > > > the place when it's normal out-of-scope place is. Is this true ?
    > > > > > Example:
    > > > > > I have a lock/unlock wrapped into the object SafeLock,


    > > > > > where SafeLock::SafeLock() { m_lock.Lock(); }
    > > > > > and SafeLock::~SafeLock() { m_lock.Unlock(); }
    > > > > > void Class::Method(void) {
    > > > > > SafeLock lock; // lock in ctor, unlocks in dtor
    > > > > > ......
    > > > > > // (1) lock is automatically unlocked here, ok.
    > > > > > }


    > > > > > So far, so good. The lock is unlocked at point (1), when it goes out
    > > > > > of scope.


    > > > > > Now let's look at the more complex case, with innner block:


    > > > > > void Class::Method(void) {
    > > > > > ......
    > > > > > { // inner block
    > > > > > SafeLock lock; // (4)
    > > > > > } // (5)
    > > > > > ... // (6)
    > > > > > }


    > > > > > Normally, lock(4) is destroyed at (5).


    > > > > > But I was told this is not necessarily so;
    > > > > > that C++ compiler is free to delay destruction of lock
    > > > > > until later, until end of bigger bklock at (6).


    > > > > > Is this true ? Is it indeed allowd ? I have difficulty
    > > > > > to believe that standard allows this. Indeed,
    > > > > > destructors can have side effects like closing files.


    > > > > The compiler could delay the destruction of the object
    > > > > if the overall behavior is the same (as you said, if
    > > > > there is no side effect).


    > > > > You are right, the lock is unlocked at (5).


    > > > > > Is it possible to have standard reference that
    > > > > > explicitly prohibits this "optimization" ?


    > > > > The relevant parts are §12.4-10:
    > > > > Destructors are invoked implicitely [...] for a
    > > > > constructed object with automatic storage duration
    > > > > (3.7.2) when the block in which the object exits [...]


    > > > > And §3.7.2-3:
    > > > > If a named object has initialization or a destructor with side effects,
    > > > > it shall not be destroyed before the end of its block


    > > > ^^^^^^
    > > > Hmmm stange wording. It says "not before".
    > > > "Shall not be destroyed before end of its block".
    > > > But it does not say "not after".


    > > Well, I believe the standard is quite clear - and all was quoted by
    > > Michael:


    > > > > Destructors are invoked implicitely [...] for a
    > > > > constructed object with automatic storage duration
    > > > > (3.7.2) when the block in which the object exits [...]


    > > and:


    > > > > If a named object has initialization or a destructor
    > > > > with side effects, it shall not be destroyed before the
    > > > > end of its block


    > > so it must not be destroyed before ever - not even if it looks as it
    > > is unused.


    > > So as Michael said, it is destroyed at exactly the point where the
    > > scope ends.


    > > So the destructor is implicitly invoked when the block exits.


    > > > Shall we interpreted it as permission to invoke the
    > > > destructor with side effects *after* end of the block ?
    > > > What good would be this for ?


    > > > I can see situations where this could be bad.


    > > > > , nor shall it be eliminated as an optimization even if
    > > > > it appears to be unused, except that a class object or
    > > > > its copy may be eliminated as specified in 12.8.

    > > So the destructor is implicitly invoked when the block
    > > exits.


    > I do not see what you see. Optimizers are known to shuffle pieces of
    > code around.


    > People *observed* the destructors to be invoked at the end and
    > *bigger* block because optimizer moved it and supposedly
    > because standard does not disabllow it such optimizations
    > *especially* for no-side-effects destructor.


    > I do not mind optimizer shuffling around the no-side-effect
    > destructors (provided that behaviour is preserved).


    > For side-effects destructor, Michael showed that standard
    > disallows moving the destructor invocation to *earlier* point.


    Which is really an unnecessary clarification---the standard
    defines precisely at what point the destructor should be called.
    A compiler is only allowed to change it if it can prove that the
    observable effects of the program are not modified.

    The reason the words are there is because it is a common
    optimization to detect and remove dead variables, i.e. variables
    that are no longer "used". The standard here is making it clear
    that if the variable has a non-trivial destructor, the compiler
    must consider that it is "used" until its normal place of
    destruction, even if there is no visible evidence of "use".

    > To me, that looks like permission for the optimizer to move
    > destructor invocation to the later point, for side-effects
    > dtors, and
    > (2) to move the no-side-effects dtors invocation forward or
    > backward (conditioned that it preserves results and behaviour,
    > of course).


    No. The sentence which is bothering you is there for authors of
    optimizers, to remind them that "use", here, has a special
    meaning in the case of objects with non-trivial destructors,
    which must be taken into account in dead variable analysis.
    (Don't forget that many of the authors of the standard are
    compiler writers, who tend to think in these terms.)
    Technically, it neither gives nor takes away any liberty that
    wasn't there otherwise. The standard states exactly when the
    destructor is called. The abstract machine which the standard
    uses to define the semantics of a program always calls it at
    exactly that place. A conformant implementation must ensure
    that the observable behavior of the compiled program is
    identical with the observable behavior which would result in
    calling it at that place.

    (The standard doesn't say anything with regards to locking and
    unlocking mutexes, of course, but that is presumably "observable
    behavior". Even if there's really no way a program can
    tell---the fact that another thread can acquire the lock
    immediately after it is released doesn't guarantee that it will
    actually behave as if it did, and interrupt the thread which
    released it.)

    > I *do not* see where standard disallows the optimizer to move
    > the side-effect destructor to later point. Where do you see
    > this ?


    The basic definition of the semantics of a program, which
    Michael also quoted.

    > And given that people observed that optimizer moved destructor
    > invocation to the end of *bigger* block,


    Who, when and with what compiler?

    > my question still remains, is this standard conformant, and
    > does it make the diffence whether dtor has side effects or not
    > wrt standard conformance ?


    The standard has something called the "as if" rule: the compiler
    can do absolutely anything it wants, as long as the observable
    behavior is identical to what one instance of the abstract
    machine would have created, i.e. as long as it behaves "as if"
    it had conformed to the exact semantics of the abstract machine.
    It could thus move a destructor, or eliminate it entirely, if it
    could prove that its side effects do not affect the "observable
    behavior". (Note that the "observable behavior" is basically
    only the order of external function calls---basically system
    calls, except, of course, a C++ program doesn't see those---and
    accesses to volatile objects. Time is never considered
    "observable", so the fact that the program runs faster---or
    slower---need not be considered with regards to correctness.)

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Mar 6, 2008
    #10
  11. Guest

    On Mar 6, 11:44 am, James Kanze <> wrote:
    > > And given that people observed that optimizer moved destructor
    > > invocation to the end of *bigger* block,

    >
    > Who, when and with what compiler?


    It was Microsoft VC6 compiler, with optimization turned on.

    Peter Koch also wrote above:

    > But I do vaguely remember that
    > VC 6.0 had a bug that prevented us from using it with all
    > optimisations on (dont remember if the above was one of the problems).
    > If you use that compiler it is just possible that you need to be
    > careful.


    V
     
    , Mar 13, 2008
    #11
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Paul M

    Destructors...

    Paul M, Feb 5, 2004, in forum: ASP .Net
    Replies:
    2
    Views:
    435
    Alvin Bruney [MVP]
    Mar 2, 2004
  2. joe martin
    Replies:
    3
    Views:
    384
    Francis Glassborow
    Jul 16, 2003
  3. Shubhadeep

    Virtual Destructors????

    Shubhadeep, Jul 15, 2003, in forum: C++
    Replies:
    1
    Views:
    385
    Jakob Bieling
    Jul 15, 2003
  4. Rajesh Garg
    Replies:
    3
    Views:
    3,525
    Victor Bazarov
    Jul 22, 2003
  5. Mike
    Replies:
    1
    Views:
    374
    Alf P. Steinbach
    Jul 30, 2003
Loading...

Share This Page