destructors moved out of place ?

V

vikimun

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
 
M

Michael DOUBEZ

(e-mail address removed) 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
 
V

vikimun

(e-mail address removed) 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
 
P

peter koch

(e-mail address removed) a écrit :
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).
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.
 
V

vikimun

(e-mail address removed) 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.
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
 
M

Michael DOUBEZ

(e-mail address removed) a écrit :
(e-mail address removed) 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
 
V

vikimun

(e-mail address removed) a écrit :




On 5 Mar., 15:18, (e-mail address removed) wrote:
(e-mail address removed) 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
 
M

Micah Cowan

With debugger, tracing and breakpoints.

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.
 
P

peter koch

On 5 Mar., 15:18, (e-mail address removed) wrote:
(e-mail address removed) 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 [...]
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.
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
 
J

James Kanze

(e-mail address removed) 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.)
 
V

vikimun

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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,743
Messages
2,569,478
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top