Subject: Re: [ub] type punning through congruent base class?
From: Jeffrey Yasskin (jyasskin_at_[hidden])
Date: 2014-01-17 15:34:08
Note that this post from Herb arrived after
http://www.open-std.org/pipermail/ub/2014-January/000418.html but was sent
before, so the thread got a little mixed up.
On Thu, Jan 16, 2014 at 11:38 AM, Herb Sutter <hsutter_at_[hidden]> wrote:
> Richard, it cannot mean that (or if it does, IMO we have an obvious bug)
> for at least two specific reasons I can think of (below), besides the
> general reasons that it would not be sensical and would violate type safety.
We do have an obvious bug in [basic.life]p1, "The lifetime of an object of
type T begins when storage with the proper alignment and size for type T is
obtained", if we interpret "obtained" as "obtained from the memory
allocator". Even with strict uses of placement-new to change the type of
memory, placement-new doesn't "obtain" any memory. If we interpret
"obtained" as just "the programmer intends a region of storage to be
available for a T", as I think Richard is suggesting, the bug is only that
we need the wording to be clearer.
First, objects must have unique addresses. Consider, still assuming B is
> trivially constructible:
> void *p = malloc(sizeof(B));
The lifetime of a B starts some time after-or-including the malloc() call
in the above line and the access of 'pb->i' two lines down. [basic.life]p5
("Before the lifetime of an object has started ... The program has undeï¬ned
behavior if ... the pointer is used to access a non-static data member")
The assignment to 'i' might start the lifetime of an 'int' subobject, but
that's not enough to make the use of 'pb->i' defined if no 'B's lifetime
> B* pb = (B*)p;
> pb->i = 0;
The lifetime of the B *ends* when its storage is re-used for the 'short'
([basic.life]p1 "The lifetime of an object of type T ends when ... the
storage which the object occupies is reused"), as Daveed said. This happens
some time after the access in the previous line, and the assignment two
> short* ps = (short*)p;
> *ps = 0;
> This cannot possibly be construed as starting the lifetime of a B object
> and a short object, else they would have the same address, which is
> illegal. Am I missing something?
Both a B object and a short object have their lifetimes started in your
code snippet, but the lifetimes don't overlap.
Confusingly, the start of these lifetimes is *not* called out in any
particular line of code; it's implied by them. In particular, the casts
don't have any lifetime effects (contra the straw man at
http://www.open-std.org/pipermail/ub/2014-January/000406.html). The code
would be just as defined (or undefined) written as:
void *p = malloc(sizeof(B));
B* pb = (B*)p;
short* ps = (short*)p;
pb->i = 0;
*ps = 0;
As Matt alluded to in
http://www.open-std.org/pipermail/ub/2014-January/000456.html, it might be
possible to say that all lifetime effects are called out in explicit
expressions without breaking C compatibility, *if* we instead say that
accessing the members of objects with trivial constructors can be done
outside of the lifetime of such objects. I have no idea whether that would
be better or worse than saying that lifetime effects can be implied.
SG12 list run by herb.sutter at gmail.com