C++ Logo

SG12

Advanced search

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 undefined
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
has started.

> 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
lines down.

> 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.

Jeffrey



SG12 list run by herb.sutter at gmail.com