Yes, but my point is that lifetime ending will not destroy an
object.
That's not how the standard uses it (although admittedly, the
standard doesn't seem to be too consistent in this regard). The
word "destroy" doesn't occur at all in §3.8, which only speaks
of "object lifetime", but in other contexts, like §3.6.3,
"destroy" is used in opposition to "initialization", and in the
library, the effects clause of the destructor is generally
"Destroys an object of class ...".
There is one point to consider, however. The original poster's
class was a POD, with a trivial destructor, and for objects with
non-class types *or* with trivial destructors, the lifetime of
the object doesn't end until "the storage which the object
occupies is reused or releases." So in fact, yes, his code is
well defined and correct. The text of §5.2.4:
"Pseudo-destructor call" is very unclear; it says that "The only
effect is the evaluation of the postfixexpression before the dot
or arrow." Presumably, it is the () which follows which "calls"
the destructor", but §5.2.2 doesn't say anything about this
case. §12.4, on the other hand, says "Once a destructor is
invoked for an object, the object no longer exists; the behavior
is undefined if the destructor is invoked for an object whose
lifetime has ended." With no distinction with regards to
whether the destructor is trivial or not (in contradiction with
§3.8?). According to §3.8, his code is legal, because a
trivial destructor doesn't end the lifetime of the object.
According to §12.4, it is illegal, because once the destructor
is invoked, the object no longer exists---at the very least, the
call of the destructor (even though it is trivial) when the
object goes out of scope will result in undefined behavior. For
various reasons, I'm pretty sure that the intent is for his code
to have undefined behavior, but in so far as his class has a
trivial destructor, I think the standard is ambiguous.
For instance, if you are in a class type object, and the
destructor starts execution, you can still dereference "this",
and the resulting lvalue must (because that's the definition
of an lvalue) refer to an object or function. It is not raw
storage, but an object outside its lifetime.
That's something I've often wondered about myself. From all
appearences, during the execution of a constructor or
destructor, the object does have some special state, which is
neither "existing" nor "raw memory". In a constructor or a
destructor, for example, typeid returns the type of the class
being constructed or destructed, virtual access and dynamic_cast
work, the constructor or destructor can access members, and a
derived class constructor or destructor can convert its this
pointer to a pointer to base, at least implicitly (and
presumably explicitly, although I've had some problems with this
in the past). All of which is in direct contradiction with
§3.8/5. (Again, however, none of this is relevant to the
original example, since it only concerns non-POD's.)
It uses the word "destroy" and "create". At 1.8/1 it says "An
object is created by a definition (3.1), by a new-expression
(5.3.4) or by the implementation (12.2) when needed.". For
destroy, it says that in the individual sections. At 3.6.3/1
for instance: "Destructors (12.4) for initialized objects of
static storage duration (declared at block scope or at
namespace scope) are called as a result of returning from main
and as a result of calling exit (18.3). These objects are
destroyed in the reverse order of the completion of their
constructor or of the completion of their dynamic
initialization.".
Exactly. Destruction is the opposite of
initialization---calling the destructor.
My point is that after lifetime ended, it seems the object is
not destoryed yet.
It's in the process of being destroyed; it's fully destroyed
once the destructor has finished. Provided the destructor is
trivial, or maybe it's provided the object has POD type.
I'm not actually sure about this, so i say it in vague terms.
Because the Standard does not seem to say where the object is
created in the following code (one could say it is a case of
"... or by the implementation when needed", but that clearly
refers to temporary objects, and not to the following:
int *p = (int*)malloc(sizeof *p);
*p = 0;
For starting lifetime, you have to have an object first, whose
lifetime property you modify. But in the above, the storage
created by malloc has no type, but an object has properties,
including a type. And the Standard says "The properties of an
object are determined when the object is created."
So, if "starting lifetime" means "creating an object", and
"ending lifetime" means "destroying an object", i think this
issue could be solved by tweaking the wording of those
paragraphs that are affected. In particular, it seems to me
it's necessary to make "lifetime" not a property of an object,
but of a region of storage: If the region of storage is alive,
this region gets a type and the other properties of an object.
In this particular case (int is a POD), the lifetime of the
object begins when storage with proper alignment is obtained.
IMHO, this still leaves a lot of problems open, consider:
void* p = malloc( std::max(sizeof(double), sizeof(int) );
int* pi = (int*)p;
double* pd = (double*)p;
Do we have two objects, a double and an int? Both occupying the
same storage? (Storage with the proper alignment has been
obtained.) I'd have a tendancy to say that the lifetime of a
POD only starts when it has been used as a given type. And ends
when the space is used as some other type. This is partially
covered by the text "the storage which the object occupies is
reused or released." But what does "reused" really signify
here? *pi and *pd are lvalue expressions. What does §3.10/15
really mean here? Things like:
*pi = 42;
// ...
*pd = 3.14159;
are (presumably) legal, but:
*pi = 42;
printf("%f", *pd);
isn't. Does access in §3.10/15 only mean reading? And what
does this mean with regards to aliasing: I seem to recall a
similar case where g++ failed, e.g.:
void f(int* pi, double* pd)
{
printf("%d", *pi);
*pd = 3.1415;
}
f( pi, pd ); // with pi and pd as above...
But this would be a problem for those cases where a region of
storage may host multiple objects: For "string a[1];", the
array is alive as soon as storage is obtained, but its first
element is not yet alive here.
So as you see i'm not sure about this issue. That's why I
asked Juha about some paragraphs of the Standard to show me
the matters.
The issue is complex, and IMHO there are some problems with the
wording in the standard. I think the intent is clear (but that
may be simply because I've worked under this assumption for so
many years). But I think Juha was just using the word "destroy"
in a more or less everyday sense, and was really only concerned
about the fact that the object was used "after" returning from
the destructor.