C++ Logo

SG12

Advanced search

Subject: Re: [ub] type punning through congruent base class?
From: Matt Austern (austern_at_[hidden])
Date: 2014-01-16 16:05:33


On Thu, Jan 16, 2014 at 11:41 AM, Herb Sutter <hsutter_at_[hidden]> wrote:

> Matt quoted:
>
>
>
> "The lifetime of an object of type T begins when:
>
> — storage with the proper alignment and size for type T is obtained, and
>
> — if the object has non-trivial initialization, its initialization is
> complete."
>
>
>
> Perhaps the necessary fix is to strike “if the object has non-trivial
> initialization” here. I don’t see why trivial constructors are special –
> sure, they don’t do anything, but they are notionally important.
>
>
>
> A bag of memory of alignment and size suitable for T is-not-a T object
> until its (possibly trivial) ctor is called on that memory – it seems wrong
> for that to be true for all T except trivially-constructible T’s (with
> implicit universal type-punning for all types <= sizeof(T)).
>

That's an interesting possibility that I hadn't considered. It's definitely
appealing in some ways, but I think that it, too, would imply some pretty
big surgery in some pretty fundamental parts of the standard. So let's
consider the following code snippet:
  struct MyPOD { int x; }; // 1
  void* vp = malloc(sizeof(MyPOD)); // 2
  MyPOD* p1 = static_cast<MyPOD*>(vp); // 3
  MyPOD* p2 = new MyPOD; // 4

So the basic question I'd ask is: after line 4, what are you allowed to do
with p1 and what are allowed to do with p2, and how do the answers differ?
We've got a couple of choices.

First choice: we decide that you aren't allowed to do anything that
involves dereferencing p1, since p1 doesn't point to an object. (No
object's lifetime has begun in line 2 or line 3. I find that unappealing. I
don't think it's the status quo, either in the standard's text, or in what
compilers do, or in what programmers expect. If we forbade it and compiler
implementers took us seriously, it would break the world.

Second choice: we decide that you're allowed to do just the same things
with p1 as you are with p2. (Except, of course, that you're required to
free p1 versus deleting p2.) So *p2 is an object and *p1 isn't, but you're
allowed to use *p1 as if it was an object.

One consequence of the second choice: we'd need to go through the standard
and make sure that the words actually say what we want. I bet we don't
currently have the notion of a contiguous region of storage that isn't an
object of type MyPOD but can be used as if it was an object of type MyPOD.
And, of course, we'd probably have to figure out what the difference is
between an actual object of type MyPOD and a region of storage that isn't
an object but is allowed to be used as if it was an object: if there isn't
any difference between the two concepts then it's a little less clear why
we need both of them.

I'm still leaning toward thinking that, after line 4, both *p1 and *p2 are
objects of type MyPOD. I don't know how the standard should say that,
though. Some change, somewhere, seems called for.

                          --Matt



SG12 list run by herb.sutter at gmail.com