Boost logo

Boost :

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.

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk