Search code examples
c++pointerscastingpointer-to-memberreinterpret-cast

casting member function pointer


I need to use a member function pointer that takes in an argument of base class that used in other code. Well, simply I want do to [something] like the example below. This code works fine, but I wonder if such cast is always safe? I cannot do dynamic or static cast here.

#include <cstdio>                                                   

class C
{                                                           
public:                                                             
        C () : c('c') {}                                            
        virtual ~C() {}                                             

        const char c;                                               
};                                                                  

class D : public C
{                                                
public:                                                             
        D () : d('d') {}                                            
        virtual ~D() {}                                             

        const char d;                                               
};                                                                  

class A 
{                                                           
public:                                                             
        A () {}                                                     
        virtual ~A() {}                                             

        void f( C& c ) { printf("%c\n",c.c); }                      
        void g( D& d ) { printf("%c %c\n",d.c,d.d); }               
};                                                                  

int main (int argc, char const* argv[])                             
{                                                                   
        void (A::*pf)( C& c ) = &A::f;                              
        void (A::*pg)( D& d ) = reinterpret_cast<void (A::*)(D&)>(&A::f);

        A a;                                                        
        C c;                                                        
        D d;                                                        

        (a.*pf)( c );                                               
        (a.*pg)( d );                                               

        return 0;                                                   
}                                                              

Solution

  • What you are trying to do cannot be done legally in C++. C++ does not support any kind of co-variance or contra-variance on function parameter types, regardless of whether this is a member function or a free function.

    In your situation the proper way to implement it is to introduce an intermediate function for parameter-type-conversion purposes

    class A 
    {                                                           
    public:          
      ...                                                   
      void f( C& c ) { printf("%c\n",c.c); }                      
      void f_with_D( D& d ) { f(d); }
      ...
    };          
    

    and make your pointer point to that intermediate function without any casts

    void (A::*pg)( D& d ) = &A::f_with_D;
    

    Now

    A a;
    D d;                                                        
    (a.*pg)( d );
    

    will ultimately call a.f with C subobject of object d as argument.

    EDIT: Yes, it will work with function overload (if I understand your question correctly). You just have to keep in mind that with function overload in order to direct the inner call to the proper version of the function you'll have to use an explicit cast

    class A 
    {                                                           
    public:          
      ...                                                   
      void f( C& c ) { printf("%c\n",c.c); }                      
      void f( D& d ) { f(static_cast<C&>(d)); }
      ...
    };          
    

    Without the cast you'll end up with A::f(D&) calling itself recursively.