Search code examples
c++language-lawyerfunction-pointerspointer-to-memberreinterpret-cast

reinterpret_cast member function pointer to void(*&)()


Member function pointers can not be reinterpret_cast to function pointers. (GCC requires the -pedantic-errors flag to enforce that, though.)

However GCC, Clang and MSVC seem to agree that casting a member function pointer to a reference to function pointer is ok. See the following example:

#include<type_traits>

struct A {
    void f() {}
};

int main() {
    auto x = &A::f;
    auto y = reinterpret_cast<void(*&)()>(x);
    // auto y = reinterpret_cast<void(*)()>(x);
    static_assert(std::is_same_v<decltype(y), void(*)()>);
}

Godbolt here

This program compiles on all three compilers, but fails to compile on all (with pedantic flags) when the out-commented line is used instead of the previous one.

I don't see any rule in the standard allowing this conversion. Is the program ill-formed and the compilers fail to diagnose it or is the program well-formed?

If the latter, what is the exact conversion sequence, where does the standard allow it and can the function pointer be converted back to the original type in order to call it or is the use of the reference for initialization of y already undefined behavior?


Solution

  • reinterpret_cast<T&>(x) is equivalent to *reinterpret_cast<T*>(&x).

    In other words, reinterpret_cast<void(*&)()>(x) performs type punning on the pointer itself.

    Accessing the result of this cast violates strict aliasing and causes undefined behavior, as usual.


    I don't see any rule in the standard allowing this conversion.

    Here:

    [expr.reinterpret.cast]/11:

    A glvalue of type T1, designating an object x, can be cast to the type “reference to T2” if an expression of type “pointer to T1” can be explicitly converted to the type “pointer to T2” using a reinterpret_­cast. The result is that of *reinterpret_­cast<T2 *>(p) where p is a pointer to x of type “pointer to T1”. ...

    Since reinterpret_cast between pointers to object types (in your case, between pointers to pointers (to [member] functions)) is always allowed, it's also allowed in your case.