title: Is clang wrong, gcc wrong, or both wrong in this case - casting away constness with member pointers
body: <p>In C++, the concept of <a href="https://timsong-cpp.github.io/cppwp/expr.const.cast#7" rel="nofollow noreferrer">casting away constness</a> is rigorously defined by the standard. Casts such as <code>static_cast</code> and <code>reinterpret_cast</code> are not allowed to cast away constness in the explicit conversion. The definition of casting away constness heavily relies on the qualification conversion definition, which says that two types can perform a qualification conversion up to <a href="https://timsong-cpp.github.io/cppwp/conv.qual#2" rel="nofollow noreferrer">similar</a> type. <code>reinterpret_cast</code> can then perform the cast on the non-similar parts, for example.</p>
  <p>Here is an example also available in <a href="https://godbolt.org/z/Y7qEcY" rel="nofollow noreferrer">godbolt</a>:</p>
  <pre class="lang-cpp prettyprint-override"><code>struct T{};
  struct F{};
  
  void f() {
      const int* const T::* const * const * const x {};
      
      // pointer to array - works in clang (trunk),
      // works in gcc (trunk)
      reinterpret_cast&lt;int* const T::* const (*) [2]&gt;(x);
  
      // member pointer to pointer - works in clang (trunk), 
      // fails in gcc (trunk)
      reinterpret_cast&lt;int* const * const * const * const&gt;(x);
  
      // member pointer to another member pointer type - fails in clang (trunk),
      // fails in gcc (trunk)
      reinterpret_cast&lt;int* const F::* const * const * const&gt;(x);
  }
  </code></pre>
  <p>Recall the definition of similar. <strong>The types at each level must be of the categories</strong>:</p>
  <blockquote>
  <p>... each Pi is “pointer to” ([dcl.ptr]), “pointer to member of class Ci of type” ([dcl.mptr]), “array of Ni”, or “array of unknown bound of” ([dcl.array])</p>
  </blockquote>
  <p>To be <strong>similar</strong>, each <strong>Pi must belong to the same category:</strong></p>
  <blockquote>
  <p>... corresponding Pi components are either the same or one is “array of Ni” and the other is “array of unknown bound of”</p>
  </blockquote>
  <p>In the first case, array and pointers are NOT similar. GCC and clang correctly says that the types <code>X[]</code> and <code>X*</code> are not similar and thus <code>reinterpret_cast</code> kicks in to ignore any const qualifiers past the array (so <code>const int* const</code> could be converted to <code>int*</code>). Everything is good.</p>
  <p>In the second case, pointers and member pointers should NOT be similar. clang correctly says that the types are not similar and thus <code>reinterpret_cast</code> kicks in. <em>GCC says the types are similar and thus we are casting away constness</em>. <strong>I'm pretty sure GCC is wrong here.</strong></p>
  <p>In the third case, we are comparing <strong>two member pointers of different classes</strong>. Both clang and GCC believes they are similar, but if we carefully look at the draft, it says <strong>&quot;pointer to member of class Ci of type&quot;</strong>. If they're of different types, shouldn't we consider them to be not-similar? <strong>Are both clang and gcc wrong here?</strong></p>
  <p><strong>By the way, MSVC allows all of the above with no warnings (x64 msvc 19.27 on godbolt).</strong></p>
  <p>EDIT: The third case can be reproduced in a simpler way:</p>
  <pre><code>const int T::* f;
  reinterpret_cast&lt;int F::*&gt;(f);
  </code></pre>