<div dir="ltr">I&#39;d like to complicate things further, in response to the idea that the snippet involving &#39;B&#39; and &#39;short&#39; is type-punning and that we should consider a type-safe mode. :)<br><br>First, consider whether the following code, intended to be completely normal C code:<br>

<br><font face="courier new, monospace">  B* pb = (B*)malloc(sizeof(B));<br>  pb-&gt;i = 0;<br>  free(pb);<br>  short* ps = (short*)malloc(sizeof(short));<br>  *ps = 0;<br>  free(ps);</font><br><br>looks like code that should be valid in this type-safe mode. If you&#39;d like to ban it, the rest of this post won&#39;t have anything interesting for you.<br>

<br>Now imagine I write a library (please forgive compile and logic errors, and typos):<br><br><font face="courier new, monospace">std::map&lt;size_t, std::stack&lt;void*&gt;&gt; size_classes = {{16, {}}, {32, {}}, ...};<br>

<br>void* my_malloc(size_t size) {<br>  auto size_class = size_classes.lower_bound(size);<br>  assert(size_class != size_classes.end());<br>  if (size_class-&gt;second.empty())<br>    return malloc(size_class-&gt;first);<br>

  void* result = size_class-&gt;second.top();<br>  size_class-&gt;second.pop();<br>  return result;<br>}<br><br>void my_free(size_t size, void* block) {<br>  size_classes.lower_bound(size)-&gt;second.push(block);<br>}</font><br>

<br>Then I use it like:<br><br><font face="courier new, monospace">  B* pb = (B*)my_malloc(sizeof(B));<br>  pb-&gt;i = 0;<br>  my_free(sizeof(B), pb);<br>  short* ps = (short*)my_malloc(sizeof(short));<br>  *ps = 0;<br>  my_free(sizeof(short), ps);</font><br>

<br>Is this worse than the above malloc/free-based code? That is, can users write wrappers around malloc and free?<br><br>But the compiler can inline the my_malloc use down to:<br><br><font face="courier new, monospace">  void *p = malloc(16);  // Probably 16.<br>

  B* pb = (B*)p;<br>  pb-&gt;i = 0;<br>  short* ps = (short*)pb;<br>  *ps = 0;</font><br><br>using knowledge of the behavior of std::map and std::stack, which is nearly identical to the &quot;type-punning&quot; code. So how do we make the type-punning invalid without breaking standard malloc-based code or user-written libraries?<br>

<br>On Fri, Jan 17, 2014 at 2:56 PM, Herb Sutter &lt;<a href="mailto:hsutter@microsoft.com">hsutter@microsoft.com</a>&gt; wrote:<br>&gt;&gt; Note that this post from Herb arrived after<br>&gt;&gt; <a href="http://www.open-std.org/pipermail/ub/2014-January/000418.html">http://www.open-std.org/pipermail/ub/2014-January/000418.html</a> but was sent<br>

&gt;&gt; before, so the thread got a little mixed up.<br>&gt;<br>&gt; Yes, I&#39;ve been trying to reply less on this thread until that sync&#39;ed back<br>&gt; up. :)<br>&gt;<br>&gt; From what I&#39;ve learned in this thread, the (rough) intended C++ model for<br>

&gt; PODs (assuming memory of the right size/alignment) would seem to be &quot;the<br>&gt; lifetime of a B starts when you write to the memory as a B, and ends when<br>&gt; you free the memory or write to the memory as different type.&quot; [Disclaimer:<br>

&gt; I&#39;m not sure if &quot;read from the memory as a B&quot; also starts lifetime.&quot;]<br>&gt;<br>&gt; I think we can do better, but it seems like that&#39;s the (rough) intent of the<br>&gt; status quo, leaving aside the question of whether the wording actually says<br>

&gt; that.<br>&gt;<br>&gt; *If* that is the (rough) intent, then in:<br>&gt;<br>&gt;&gt;   void *p = malloc(sizeof(B)); // 1<br>&gt;&gt;<br>&gt;&gt;   B* pb = (B*)p; // 2<br>&gt;&gt;<br>&gt;&gt;   pb-&gt;i = 0; // 3<br>&gt;&gt;<br>

&gt;&gt;   short* ps = (short*)p; // 4<br>&gt;&gt;   *ps = 0; // 5<br>&gt;&gt;<br>&gt;&gt;   free(p); // 6<br>&gt;<br>&gt;<br>&gt; I assume that the reasoning would be that:<br>&gt;<br>&gt; line 3 starts the lifetime of a B (we&#39;re writing to the bits of a B member,<br>

&gt; not just any int)<br>&gt; line 5 ends the lifetime of that B and begins the lifetime of a short<br>&gt; line 6 ends the lifetime of that short<br>&gt;<br>&gt;<br>&gt; Again ignoring whether this is desirable, is that (roughly) the intent of<br>

&gt; the current wording?<br>&gt;<br>&gt;<br>&gt; If yes, does the wording express it (a) accurately and (b) clearly?<br>&gt;<br>&gt;<br>&gt; Finally, regardless of the above answer, do we want to change anything about<br>

&gt; the legality or semantics of the above type-punning code, such as possibly<br>&gt; having a &quot;type-safe mode&quot; where such code is somehow not allowed unless in<br>&gt; an &quot;extern &quot;C-compat&quot;&quot; block or something?<br>

&gt;<br>&gt;<br>&gt; Herb<br>&gt;<br>&gt;<br>&gt;<br>&gt; ________________________________<br>&gt; From: <a href="mailto:ub-bounces@open-std.org">ub-bounces@open-std.org</a> &lt;<a href="mailto:ub-bounces@open-std.org">ub-bounces@open-std.org</a>&gt; on behalf of Jeffrey<br>

&gt; Yasskin &lt;<a href="mailto:jyasskin@google.com">jyasskin@google.com</a>&gt;<br>&gt; Sent: Friday, January 17, 2014 1:34 PM<br>&gt;<br>&gt; To: WG21 UB study group<br>&gt; Subject: Re: [ub] type punning through congruent base class?<br>

&gt;  <br>&gt; Note that this post from Herb arrived after<br>&gt; <a href="http://www.open-std.org/pipermail/ub/2014-January/000418.html">http://www.open-std.org/pipermail/ub/2014-January/000418.html</a> but was sent<br>

&gt; before, so the thread got a little mixed up.<br>&gt;<br>&gt; On Thu, Jan 16, 2014 at 11:38 AM, Herb Sutter &lt;<a href="mailto:hsutter@microsoft.com">hsutter@microsoft.com</a>&gt; wrote:<br>&gt;&gt;<br>&gt;&gt; Richard, it cannot mean that (or if it does, IMO we have an obvious bug)<br>

&gt;&gt; for at least two specific reasons I can think of (below), besides the<br>&gt;&gt; general reasons that it would not be sensical and would violate type safety.<br>&gt;<br>&gt;<br>&gt; We do have an obvious bug in [basic.life]p1, &quot;The lifetime of an object of<br>

&gt; type T begins when storage with the proper alignment and size for type T is<br>&gt; obtained&quot;, if we interpret &quot;obtained&quot; as &quot;obtained from the memory<br>&gt; allocator&quot;. Even with strict uses of placement-new to change the type of<br>

&gt; memory, placement-new doesn&#39;t &quot;obtain&quot; any memory. If we interpret<br>&gt; &quot;obtained&quot; as just &quot;the programmer intends a region of storage to be<br>&gt; available for a T&quot;, as I think Richard is suggesting, the bug is only that<br>

&gt; we need the wording to be clearer.<br>&gt;<br>&gt;&gt; First, objects must have unique addresses. Consider, still assuming B is<br>&gt;&gt; trivially constructible:<br>&gt;&gt;<br>&gt;&gt;   void *p = malloc(sizeof(B));<br>

&gt;<br>&gt;<br>&gt; The lifetime of a B starts some time after-or-including the malloc() call in<br>&gt; the above line and the access of &#39;pb-&gt;i&#39; two lines down. [basic.life]p5<br>&gt; (&quot;Before the lifetime of an object has started ... The program has undefined<br>

&gt; behavior if ... the pointer is used to access a non-static data member&quot;)<br>&gt;<br>&gt; The assignment to &#39;i&#39; might start the lifetime of an &#39;int&#39; subobject, but<br>&gt; that&#39;s not enough to make the use of &#39;pb-&gt;i&#39; defined if no &#39;B&#39;s lifetime has<br>

&gt; started.<br>&gt;  <br>&gt;&gt;<br>&gt;&gt;   B* pb = (B*)p;<br>&gt;&gt;   pb-&gt;i = 0;<br>&gt;<br>&gt;<br>&gt; The lifetime of the B *ends* when its storage is re-used for the &#39;short&#39;<br>&gt; ([basic.life]p1 &quot;The lifetime of an object of type T ends when ... the<br>

&gt; storage which the object occupies is reused&quot;), as Daveed said. This happens<br>&gt; some time after the access in the previous line, and the assignment two<br>&gt; lines down.<br>&gt;  <br>&gt;&gt;<br>&gt;&gt;   short* ps = (short*)p;<br>

&gt;&gt;   *ps = 0;<br>&gt;&gt;<br>&gt;&gt; This cannot possibly be construed as starting the lifetime of a B object<br>&gt;&gt; and a short object, else they would have the same address, which is illegal.<br>&gt;&gt; Am I missing something?<br>

&gt;<br>&gt;<br>&gt; Both a B object and a short object have their lifetimes started in your code<br>&gt; snippet, but the lifetimes don&#39;t overlap.<br>&gt;<br>&gt; Confusingly, the start of these lifetimes is *not* called out in any<br>

&gt; particular line of code; it&#39;s implied by them. In particular, the casts<br>&gt; don&#39;t have any lifetime effects (contra the straw man at<br>&gt; <a href="http://www.open-std.org/pipermail/ub/2014-January/000406.html">http://www.open-std.org/pipermail/ub/2014-January/000406.html</a>). The code<br>

&gt; would be just as defined (or undefined) written as:<br>&gt;<br>&gt;   void *p = malloc(sizeof(B));<br>&gt;<br>&gt;   B* pb = (B*)p;<br>&gt;   short* ps = (short*)p;<br>&gt;   pb-&gt;i = 0;<br>&gt;<br>&gt;   *ps = 0;<br>

&gt;<br>&gt;<br>&gt; As Matt alluded to in<br>&gt; <a href="http://www.open-std.org/pipermail/ub/2014-January/000456.html">http://www.open-std.org/pipermail/ub/2014-January/000456.html</a>, it might be<br>&gt; possible to say that all lifetime effects are called out in explicit<br>

&gt; expressions without breaking C compatibility, *if* we instead say that<br>&gt; accessing the members of objects with trivial constructors can be done<br>&gt; outside of the lifetime of such objects. I have no idea whether that would<br>

&gt; be better or worse than saying that lifetime effects can be implied.<br>&gt;<br>&gt;<br>&gt; Jeffrey<br>&gt;<br>&gt;<br>&gt; _______________________________________________<br>&gt; ub mailing list<br>&gt; <a href="mailto:ub@isocpp.open-std.org">ub@isocpp.open-std.org</a><br>

&gt; <a href="http://www.open-std.org/mailman/listinfo/ub">http://www.open-std.org/mailman/listinfo/ub</a><br>&gt;<br></div>