$include_dir="/home/hyper-archives/boost/include"; include("$include_dir/msg-header.inc") ?>
From: Anthony Williams (anthony.williamsNOSPAM_at_[hidden])
Date: 2002-12-05 05:05:01
Gabriel Dos Reis writes:
> "Peter Dimov" <pdimov_at_[hidden]> writes:
>
> | From: "Gabriel Dos Reis" <gdr_at_[hidden]>
> | > David Abrahams <dave_at_[hidden]> writes:
> | >
> | > | Gabriel Dos Reis <gdr_at_[hidden]> writes:
> | > |
> | > | > Hmm, I have a couple of questions answers to which will help me
> | > | > get your point.
> | > | >
> | > | > 1) Why can't you do that with reinterpret_cast?
> | > |
> | > | You can, but the results are non-portable
> | >
> | > No more non-portable than with dangerous_cast<>.
> |
> | I think that Dave's point is that in
> |
> | new(h.storage) Foo;
> |
> | there is a char* -> void* implicit conversion as placement new takes a
> | void*. So the placement new performs char* -> void* -> Foo* (by constructing
> | a Foo at (void*)h.storage), which - in theory - might not be the same as
> | char* -> Foo*.
>
> But then, that theoretical implementation can remember the type from
> which the conversion to void* was made and may issue an error when an
> attempt is made to dereference the pointer obtained by recasting the
> result of static_cast<void*>(h.storage) to Foo*.
>
> Practical notes:
> Historically, char* was used as the type of "raw memory" until "void*"
> was invented. And since then char* and void* continues to have the
> same properties as raw-memory issues are concerned.
unsigned char* has _additional_ properties to void* --- you can access the
object representation of _any_ object through an unsigned char* (and for PODs,
you can copy them around using this)
3.9p4:
"The object representation of an object of type T is the sequence of N
unsigned char objects taken up by the object of type T, where N equals
sizeof(T)."
3.10p15:
"If a program attempts to access the stored value of an object through an
lvalue of other than one of the following types the behavior is undefined:
- the dynamic type of the object,
...
- a char or unsigned char type."
So given a Foo object foo, static_cast<char*>(static_cast<void*>(&foo)) is
legal, and can be used to access the object representation of the object.
Also, 3.9.2p4 says:
"Objects of cvqualified (3.9.3) or cvunqualified type void* (pointer to
void), can be used to point to objects of unknown type. A void* shall be
able to hold any object pointer. A cvqualified or cvunqualified (3.9.3)
void* shall have the same representation and alignment requirements as a
cvqualified or cvunqualified char*."
So casting a void* to/from a char* is a no-op.
3.8p1:
"The lifetime of an object is a runtime property of the object. The lifetime
of an object of type T begins when:
- storage with the proper alignment and size for type T is obtained, and
- if T is a class type with a nontrivial constructor (12.1), the constructor
call has completed."
Thus, given that h.storage is properly aligned, (which is the purpose of the
other union member), after "new(h.storage) Foo", h.storage contains a Foo
object. Thus accessing it through a pointer-to-Foo is legal, as Foo is the
dynamic type of the object.
The question is: is the Foo* returned by the placement new expression (which
is usable) the same as the Foo* returned from
static_cast<Foo*>(static_cast<void*>(h.storage))?
The object representation of the Foo object is the sizeof(T) bytes starting at
static_cast<void*>(h.storage) (since that is what was passed to placement
new), so static_cast<void*>(pfoo)==static_cast<void*>(h.storage) if pfoo is
the value returned from the new expression.
Thus
static_cast<Foo*>(static_cast<void*>(pfoo))
==static_cast<Foo*>(static_cast<void*>(h.storage))
==pfoo
and we're legal.
Anthony
-- Anthony Williams Senior Software Engineer, Beran Instruments Ltd. Remove NOSPAM when replying, for timely response.