C++ Logo

SG12

Advanced search

Subject: Re: [ub] type punning through congruent base class?
From: James Dennett (jdennett_at_[hidden])
Date: 2014-01-16 14:46:57


On Thu, Jan 16, 2014 at 12:24 PM, David Vandevoorde <daveed_at_[hidden]> wrote:
>
> On Jan 16, 2014, at 3:17 PM, Herb Sutter <hsutter_at_[hidden]> wrote:
> [...]
>> As I pointed out in other email that might have been delayed by moderation, if in the above code any lifetime began, then we have a contradiction in the standard because we would be in the untenable position that two objects can have the same address:
>>
>> struct B { int x };
>> void* p = malloc(sizeof(B));
>>
>> B* pb = (B*)p;
>> pb->x = 17;
>>
>> short* ps = (short*)p
>> *ps = 17;
>>
>> None of this code can be viewed as starting a lifetime. Otherwise, proof by contradiction (meaning contradiction in the standard): B and short are both trivially-constructible. If any of these lines start a lifetime of either a B or a short on the grounds that they are trivially-constructible, then this code must start the life of *both* a B and a short, and then *pb and *ps have the same address, which is a contradiction. Therefore none of these lines start a lifetime, QED.
>> Am I missing something?
>
> I think so: 3.8/1 also says that the lifetime of an object ends if its storage is reused. So when you write to *ps, you've terminated the lifetime of *pb.

If the lifetime started when the storage was allocated, then there's
still a window up to the write to *ps where the lifetimes of both
objects have started and not ended. (And we can assume that the
lifetime of objects of _every_ type compatible with the size and
alignment of the storage started at the same address at the same time,
because the standard kind of says so.)

C tries to have wording around "effective types", which change the
types based on assignments and memcpy/memmove: "If a value is stored
into an object having no declared type through an lvalue having a type
that is not a character type, then the type of the lvalue becomes the
effective type of the object for that access and for subsequent
accesses that do not modify the stored value. If a value is copied
into an object having no declared type using memcpy or memmove, or is
copied as an array of character type, then the effective type of the
modified object for that access and for subsequent accesses that do
not modify the value is the effective type of the object from which
the value is copied, if it has one. For all other accesses to an
object having no declared type, the effective type of the object is
simply the type of the lvalue used for the access."

In C++ we'd like to say that _assignment_ must be to an object that
already exists, which pushes us towards the only other possible
candidate for starting the lifetime, which is the allocation. That
doesn't work out well either.

Trying to merge the model of C (effective type established by
declarations or by assignments or memcpy/memmove, no explicit
initialization/destruction step) with the newer model of C++ (lifetime
controlled by initialization/destruction) is decidedly non-trivial.

-- James


SG12 list run by herb.sutter at gmail.com