title: What is the modern, correct way to do type punning in C++?
body: <p>It seems like there are two types of C++. The practical C++ and the language lawyer C++. In certain situations, it can be useful to be able to interpret a bit pattern of one type as if it were a different type. Floating-point tricks are a notable example. Let's take the famous fast inverse square root (taken from <a href="https://en.wikipedia.org/wiki/Fast_inverse_square_root" rel="noreferrer">Wikipedia</a>, which was in turn taken from <a href="https://github.com/id-Software/Quake-III-Arena/blob/master/code/game/q_math.c#L552" rel="noreferrer">here</a>):</p>
  <pre class="lang-c prettyprint-override"><code>float Q_rsqrt( float number )
  {
      long i;
      float x2, y;
      const float threehalfs = 1.5F;
  
      x2 = number * 0.5F;
      y  = number;
      i  = * ( long * ) &amp;y;                       // evil floating point bit level hacking
      i  = 0x5f3759df - ( i &gt;&gt; 1 );               // what the
      y  = * ( float * ) &amp;i;
      y  = y * ( threehalfs - ( x2 * y * y ) );   // 1st iteration
  //  y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed
  
      return y;
  }
  </code></pre>
  <p>Setting aside details, it uses certain properties of the IEEE-754 floating-point bit representation. The interesting part here is the <code>*(long*)</code> cast from <code>float*</code> to <code>long*</code>. There are differences between C and C++ about which types of such reinterpreting casts are defined behavior, however in practice such techniques are used often in both languages.</p>
  <p>The thing is that for such a simple problem there are a lot of pitfalls that can occur with the approach presented above and different others. To name some:</p>
  <ul>
  <li><a href="https://stackoverflow.com/a/37119041/12501684">unspecified behavior</a></li>
  <li><a href="https://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html" rel="noreferrer">strict aliasing</a></li>
  <li><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p0593r6.html" rel="noreferrer">lifetime issues</a></li>
  <li>endianness</li>
  <li>alignment</li>
  </ul>
  <p>At the same time, there are a lot of ways of performing type punning and a lot of mechanisms related to it. These are all that I could find:</p>
  <ul>
  <li><p><code>reinterpret_cast</code> and c-style cast</p>
  <pre class="lang-cpp prettyprint-override"><code>[[nodiscard]] float int_to_float1(int x) noexcept
  {
      return *reinterpret_cast&lt;float*&gt;(&amp;x);
  }
  [[nodiscard]] float int_to_float2(int x) noexcept
  {
      return *(float*)(&amp;x);
  }
  </code></pre>
  </li>
  <li><p><code>static_cast</code> and <code>void*</code></p>
  <pre class="lang-cpp prettyprint-override"><code>[[nodiscard]] float int_to_float3(int x) noexcept
  {
      return *static_cast&lt;float*&gt;(static_cast&lt;void*&gt;(&amp;x));
  }
  </code></pre>
  </li>
  <li><p><code>std::bit_cast</code></p>
  <pre class="lang-cpp prettyprint-override"><code>[[nodiscard]] constexpr float int_to_float4(int x) noexcept
  {
      return std::bit_cast&lt;float&gt;(x);
  }
  </code></pre>
  </li>
  <li><p><code>memcpy</code></p>
  <pre class="lang-cpp prettyprint-override"><code>[[nodiscard]] float int_to_float5(int x) noexcept
  {
      float destination;
      memcpy(&amp;destination, &amp;x, sizeof(x));
      return destination;
  }
  </code></pre>
  </li>
  <li><p><code>union</code></p>
  <pre class="lang-cpp prettyprint-override"><code>[[nodiscard]] float int_to_float6(int x) noexcept
  {
      union {
          int as_int;
          float as_float;
      } destination{x};
      return destination.as_float;
  }
  </code></pre>
  </li>
  <li><p>placement <code>new</code> and <code>std::launder</code></p>
  <pre class="lang-cpp prettyprint-override"><code>[[nodiscard]] float int_to_float7(int x) noexcept
  {
      new(&amp;x) float;
      return *std::launder(reinterpret_cast&lt;float*&gt;(&amp;x));
  }
  </code></pre>
  </li>
  <li><p><code>std::byte</code></p>
  <pre class="lang-cpp prettyprint-override"><code>[[nodiscard]] float int_to_float8(int x) noexcept
  {
      return *reinterpret_cast&lt;float*&gt;(reinterpret_cast&lt;std::byte*&gt;(&amp;x));
  }
  </code></pre>
  </li>
  </ul>
  <p>The question is which of these ways are safe, which are unsafe, and which are damned forever. Which one should be used and why? Is there a canonical one accepted by the C++ community? Why are new versions of C++ introducing even more mechanisms <code>std::launder</code> in C++17 or <code>std::byte</code>, <code>std::bit_cast</code> in C++20?</p>
  <p>To give a concrete problem: <strong>what would be the safest, most performant, and best way to rewrite the fast inverse square root function?</strong> (Yes, I know that there is a suggestion of one way on Wikipedia).</p>
  <p><strong>Edit</strong>: To add to the confusion, it seems that there is <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0593r5.html" rel="noreferrer">a proposal</a> that suggests adding yet another type punning mechanism: <code>std::start_lifetime_as</code>, which is also discussed in <a href="https://stackoverflow.com/questions/58254009/any-useful-difference-between-stdbit-cast-and-stdstart-lifetime-as">another question</a>.</p>
  <p>(<a href="https://godbolt.org/z/r1eo331rs" rel="noreferrer">godbolt</a>)</p>