<div dir="ltr"><a href="http://eel.is/c++draft/basic#:~:text=If%20two%20objects,end%20note%5D">http://eel.is/c++draft/basic#:~:text=If%20two%20objects,end%20note%5D</a> That is what reinterpret_cast says about 

pointer interconvertible<div><br></div><div><a href="http://eel.is/c++draft/ptr.launder#2">http://eel.is/c++draft/ptr.launder#2</a> std::launder clearly allows to get a &quot;pointer to T&quot; given *the address* where T&#39;s storage starts<div><br></div><div><br></div></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, Aug 8, 2023 at 9:05 PM Julien Villemure-Fréchette via Std-Discussion &lt;<a href="mailto:std-discussion@lists.isocpp.org">std-discussion@lists.isocpp.org</a>&gt; wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<div><div dir="auto">This seems strange. std::launder explicitly says it cannot convert a pointer to &#39;o1&#39; to a pointer to &#39;o2&#39; unless &#39;o1&#39; and &#39;o2&#39; are pointer interconvertible, otherwise it is UB.<br><br>from my understanding, I thought that std::launder was only an escape hatch for dealing with stale references to non transparently replaceable objects.</div><br><br><div class="gmail_quote"><div dir="auto">On August 6, 2023 1:20:00 p.m. EDT, Mykola Garkusha via Std-Discussion &lt;<a href="mailto:std-discussion@lists.isocpp.org" target="_blank">std-discussion@lists.isocpp.org</a>&gt; wrote:</div><blockquote class="gmail_quote" style="margin:0pt 0pt 0pt 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<div dir="ltr">Thanks, this is super clear actually. Everything settled after deep diving into pointer conversions for non-pointer-interconvertible objects.<div><br></div><div>So then with std::launder, this shall work as it should return pointer value &quot;pointer to T&quot;?</div><div><br></div><div><span style="color:rgb(49,54,56)">T2* ptr2 = std::laudner(reinterpret_cast&lt;T2 *&gt;(&amp;storage_)) </span></div><div><span style="color:rgb(49,54,56)">ptr2-&gt;value... </span></div><div><span style="color:rgb(49,54,56)"><br></span></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Sun, Aug 6, 2023 at 5:39 PM Matthew House via Std-Discussion &lt;<a href="mailto:std-discussion@lists.isocpp.org" target="_blank">std-discussion@lists.isocpp.org</a>&gt; wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">On Sun, Aug 6, 2023 at 10:03 AM Mykola Garkusha<br>
&lt;<a href="mailto:garkusha.mykola@gmail.com" target="_blank">garkusha.mykola@gmail.com</a>&gt; wrote:<br>
&gt; Just want to clarify this clearly to be 100% sure.<br>
&gt;<br>
&gt; &gt; Not in general; [basic.lval] para. 11 is specifically about reading<br>
&gt; &gt; and writing scalar values through a glvalue, and nothing else. Since<br>
&gt; &gt; ptr2 is a T* pointer pointing to an std::byte object, you can&#39;t do<br>
&gt; &gt; most things with ptr2 that you could do with an arbitrary T* pointer,<br>
&gt;<br>
&gt; ptr2 should point to T2 now<br>
&gt;<br>
&gt; <a href="https://eel.is/c++draft/expr#reinterpret.cast-7" rel="noreferrer" target="_blank">https://eel.is/c++draft/expr#reinterpret.cast-7</a><br>
&gt; <a href="https://eel.is/c++draft/expr#reinterpret.cast-3" rel="noreferrer" target="_blank">https://eel.is/c++draft/expr#reinterpret.cast-3</a><br>
<br>
That&#39;s not how it works in this case, since the two objects &#39;storage_&#39;<br>
and &#39;*ptr_&#39; are not pointer-interconvertible. By<br>
[expr.reinterpret.cast] para. 7, &#39;T* ptr2 =<br>
reinterpret_cast&lt;T*&gt;(&amp;storage_)&#39; is equivalent to &#39;T* ptr2 =<br>
static_cast&lt;T*&gt;(static_cast&lt;void*&gt;(&amp;storage_))&#39;. Here, &#39;&amp;storage_&#39;<br>
points to the entire &#39;storage_&#39; array (not to its first element<br>
&#39;storage_[0]&#39;, as I mistakenly said earlier), so by [expr.static.cast]<br>
para. 4 and [conv.ptr] para. 2, &#39;static_cast&lt;void*&gt;(&amp;storage_)&#39; also<br>
points to the &#39;storage_&#39; array.<br>
<br>
For &#39;T* ptr2 = static_cast&lt;T*&gt;(...)&#39;, we have to look at<br>
[expr.static.cast] para. 14: *if* there exists an object of type<br>
(similar to) T pointer-interconvertible with &#39;storage_&#39;, then &#39;ptr2&#39;<br>
will point to that T object; *otherwise*, &#39;ptr2&#39; will be a T* pointer<br>
pointing to the &#39;storage_&#39; array. But if we look at the definition of<br>
pointer-interconvertible objects in [basic.compound] para. 4, none of<br>
those bullets apply for &#39;storage_&#39; and &#39;*ptr_&#39;: they are different<br>
objects, neither is a union object, and neither is the first<br>
non-static member of the other. Therefore, &#39;ptr2&#39; is a T* pointer<br>
pointing to the std::byte[sizeof(T)] array &#39;storage_&#39;, not a T*<br>
pointer pointing to the T object &#39;*ptr_&#39;.<br>
<br>
&gt; Not sure how strict aliasing (<a href="https://eel.is/c++draft/expr#basic.lval-11" rel="noreferrer" target="_blank">https://eel.is/c++draft/expr#basic.lval-11</a>) can be violated in the example below, as the access (for scalar variables) would be done through dynamic type, as T2 lifetime has started and storage_ provides storage for it.<br>
&gt; T2* ptr2 = reinterpret_cast&lt;T2 *&gt;(&amp;storage_)<br>
&gt; ptr2-&gt;value/*scalar*/;<br>
&gt;<br>
&gt; &gt; ([expr.ref] para. 8). Most (but not all) usual<br>
&gt; &gt; operations require that the dynamic type of the pointee actually<br>
&gt; &gt; matches the type of the pointer.<br>
&gt;<br>
&gt; I don&#39;t see how the above violates the sample below.<br>
&gt;  reinterpret_cast&lt;T2 *&gt;(&amp;storage_)-&gt;....<br>
<br>
Look at [expr.ref] para. 8. The class member access &#39;ptr2-&gt;value&#39; is<br>
illegal, regardless of what is done with that glvalue afterwards. The<br>
&quot;strict aliasing&quot; rule in [basic.lval] para. 11 only applies *after*<br>
that class member access is done, and even then it helps only if<br>
&#39;value&#39; is a char, unsigned char, or std::byte.<br>
<br>
&gt; The type of expression above is T* and that is what the *actual* result of reintepret_cast is as T2&#39;s object address should be equal to the address of storage_ and storage_ provides storage for T2&#39;s object which is in turn tested within storage_. Although I agree that std::launder would make it more explicit and I can be wrong on this. <a href="https://eel.is/c++draft/basic#intro.object-9" rel="noreferrer" target="_blank">https://eel.is/c++draft/basic#intro.object-9</a><br>
<br>
The std::byte[sizeof(T)] array does provide storage for the T object,<br>
but this is irrelevant here, since the array object and the T object<br>
are not pointer-interconvertible. The only importance of providing<br>
storage is that the T object is nested within the array by<br>
([intro.object] para. 4), so constructing the T object doesn&#39;t end the<br>
lifetime of the array ([basic.life] para. 1).<br>
-- <br>
Std-Discussion mailing list<br>
<a href="mailto:Std-Discussion@lists.isocpp.org" target="_blank">Std-Discussion@lists.isocpp.org</a><br>
<a href="https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion" rel="noreferrer" target="_blank">https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion</a><br>
</blockquote></div>
</blockquote></div></div>-- <br>
Std-Discussion mailing list<br>
<a href="mailto:Std-Discussion@lists.isocpp.org" target="_blank">Std-Discussion@lists.isocpp.org</a><br>
<a href="https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion" rel="noreferrer" target="_blank">https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion</a><br>
</blockquote></div>

