2

This is kind of a strange situation I've run into. I expected for the pointer to be implicitly upcast:

struct BaseClass
{};

struct DerivedClass : BaseClass
{};

void baseClassArgFunc(BaseClass* arg) {}            // Function taking BaseClass argument
void derivedClassArgFunc(DerivedClass* arg) {}      // Function taking DerivedClass argument

int main()
{
    void (*pBaseFuncArg) (BaseClass*);      // Pointer to function taking BaseClass argument
    void (*pDerivedFuncArg) (DerivedClass*);    // Pointer to function taking DerivedClass argument

    pBaseFuncArg = baseClassArgFunc;            // Assign pointer, works fine
    pDerivedFuncArg = derivedClassArgFunc;      // Assign pointer, works fine

    pBaseFuncArg = derivedClassArgFunc;         // A value of type void (*) (DerivedClass *arg) cannot be 
                                                // assigned to an entity of type void (*) (BaseClass *)

    pDerivedFuncArg = baseClassArgFunc;         // A value of type void (*) (BaseClass *arg) cannot be 
                                                // assigned to an entity of type void (*) (DerivedClass *)

    return 0;
}

I expected assigning void ( * ) (DerivedClass*) to void baseClassArgFunc(BaseClass* arg) to be safe. I'm confused. I guess there's no implicit upcasting with function pointer arguments then?

Jarod42
  • 203,559
  • 14
  • 181
  • 302
Zebrafish
  • 11,682
  • 3
  • 43
  • 119
  • **Yes**. No implicit upcasting here. – iBug Jul 29 '17 at 16:37
  • Correct. Not sure this question can really get an answer beyond "yes". Are you looking for anything more than that? –  Jul 29 '17 at 16:38
  • @hvd: Something like `compatible_cast<>` for function-pointers, member-pointers and member-function-pointers would be nice... – Deduplicator Jul 29 '17 at 16:40
  • @hvd Hmm, yes I guess so, I'm not sure whether what I was expecting was reasonable, the language has got so many ins and outs I'm not sure what to expect. – Zebrafish Jul 29 '17 at 16:42

1 Answers1

3

Changing your classes to expose the issues:

struct BaseClass
{
};

struct DerivedClass : BaseClass
{
    int a;
};

Then

pBaseFuncArg = derivedClassArgFunc;

is unsafe for:

 void derivedClassArgFunc(DerivedClass* arg) { arg->a = 42; }


 int main()
 {
     void (*pBaseFuncArg) (BaseClass*) = derivedClassArgFunc;
     BaseClass base;

     //derivedClassArgFunc(&base); // Doesn't compile as expected
     pBaseFuncArg(&base);          // Would be UB, base->i won't exist
 }
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Yeah, I was trying to think of which way would be safe or unsafe. I think the other way is safe, but it seems there's just no implicit casting here. – Zebrafish Jul 29 '17 at 16:54