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.