TransWikia.com

Is clang wrong, gcc wrong, or both wrong in this case - casting away constness with member pointers

Stack Overflow Asked by OneRaynyDay on December 21, 2020

In C++, the concept of casting away constness is rigorously defined by the standard. Casts such as static_cast and reinterpret_cast 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 similar type. reinterpret_cast can then perform the cast on the non-similar parts, for example.

Here is an example also available in godbolt:

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<int* const T::* const (*) [2]>(x);

    // member pointer to pointer - works in clang (trunk), 
    // fails in gcc (trunk)
    reinterpret_cast<int* const * const * const * const>(x);

    // member pointer to another member pointer type - fails in clang (trunk),
    // fails in gcc (trunk)
    reinterpret_cast<int* const F::* const * const * const>(x);
}

Recall the definition of similar. The types at each level must be of the categories:

… 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])

To be similar, each Pi must belong to the same category:

… corresponding Pi components are either the same or one is “array of Ni” and the other is “array of unknown bound of”

In the first case, array and pointers are NOT similar. GCC and clang correctly says that the types X[] and X* are not similar and thus reinterpret_cast kicks in to ignore any const qualifiers past the array (so const int* const could be converted to int*). Everything is good.

In the second case, pointers and member pointers should NOT be similar. clang correctly says that the types are not similar and thus reinterpret_cast kicks in. GCC says the types are similar and thus we are casting away constness. I’m pretty sure GCC is wrong here.

In the third case, we are comparing two member pointers of different classes. Both clang and GCC believes they are similar, but if we carefully look at the draft, it says "pointer to member of class Ci of type". If they’re of different types, shouldn’t we consider them to be not-similar? Are both clang and gcc wrong here?

By the way, MSVC allows all of the above with no warnings (x64 msvc 19.27 on godbolt).

EDIT: The third case can be reproduced in a simpler way:

const int T::* f;
reinterpret_cast<int F::*>(f);

One Answer

I had a discussion with Richard Smith (the creator of clang) and I reached the conclusion that all three cases should fail to compile if we pass in -pedantic and that MSVC indeed is the one with the error. This is because there exists a single cv-decomposition in each of the examples in which reinterpret_cast<> would cast away constness, and thus the operation ideally should not be able to work. However, clang has extensions that allows this to work with warnings. It's not UB per se.

Correct answer by OneRaynyDay on December 21, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP