1

I'm having a problem with g++ and how templates interact with pointers to functions. Consider the following declaration of a template.

template <class T,class B> class TestTemplate {

  private:
    T* context;

  public:
    TestTemplate(T* usingClass);

    B* testfcnOK(B* arg);
    B* testfcnBAD(B* (T::*fcn)(void));
};

template <class T,class B> TestTemplate<T,B>::TestTemplate(T* usingClass) {
  context = usingClass;
}

template <class T,class B> B* TestTemplate<T,B>::testfcnOK(B* arg) {
    return arg;
}

template <class T,class B> B* TestTemplate<T,B>::testfcnBAD(B* (T::*fcn)(void)) {
    return (context->*fcn)();
}

Think of T as a class which contains various functions which return objects of type B. The method to focus on above is testfcnBAD() since it causes the problem. Here's the code that uses this template.

class Base { };
class Derived : public Base { };

class Tester {

  public:
   TestTemplate<Tester,Base> *templateClass;

   Base* returnBase() { return new Base(); }
   Base* returnDerivedOK() { return new Derived(); }
   Derived* returnDerivedBAD() { return new Derived(); }

   void runTest()
     {
       templateClass = new TestTemplate<Tester,Base>(this);

       // These work.
       Base* baseResult = templateClass->testfcnOK(new Base());
       baseResult = templateClass->testfcnOK(new Derived());
       baseResult = templateClass->testfcnBAD(&Tester::returnBase);
       Derived* derivedResult = (Derived*) templateClass->testfcnBAD(&Tester::returnDerivedOK);

       // This does not work.
       derivedResult = (Derived*) templateClass->testfcnBAD(&Tester::returnDerivedBAD);
     }
};

When given the last line of runTest(), g++ (4.5.2) chokes. The problem seems to be that testfcnBAD() is passed a pointer to a function which returns an instance of Derived, while TestTemplate declares testfcnBAD() to take a pointer to a function which returns a Base object. It seems like this code should be OK because a Derived object is a Base object, but the fact that Derived is a sub-class of Base may be lost somewhere along the line.

Am I missing something here, and is there a work-around?

3
  • What exactly do you mean by "chokes"? Casting (Derived*) invalidly should not throw any error until you use the value. Is this a compiler error? Run time exception? We need more information. Are you compiling with RTTI?
    – VoidStar
    Commented Oct 14, 2011 at 18:51
  • here's the g++ output: test.cpp: In member function 'void Tester::runTest()': test.cpp:32:83: error: no matching function for call to 'TestTemplate<Tester, Base>::testfcnBAD(Derived* (Tester::*)())' test.h:22:31: note: candidate is: B* TestTemplate<T, B>::testfcnBAD(B* (T::*)()) [with T = Tester, B = Base] Commented Oct 14, 2011 at 18:53
  • I've removed references to templates in both title and tags as they are not relevant to your issue (which is related to initializing a Base (Tester::*)() from a Derived (Tester::*)()).
    – Luc Danton
    Commented Oct 14, 2011 at 19:27

2 Answers 2

1

I'm no expert in the C++ Standard, but from what I can tell, covariant return types only apply when overriding a function. You cannot use covariant return types with a function pointer. I would propose something like:

template <class T,class B> class TestTemplate {

  private:
    T* context;

  public:
    TestTemplate(T* usingClass);

    B* testfcnOK(B* arg);
    template<typename D> B* testfcnBAD(D* (T::*fcn)(void));
};
2
  • That seems to work, although it's still a mystery to me why this is necessary. It also weakens the type checking, which is too bad. Thanks. Commented Oct 14, 2011 at 19:30
  • @Randall "You cannot use covariant return types with a function pointer."
    – Luc Danton
    Commented Oct 14, 2011 at 19:33
0

Given your code, if you could convert a Derived *(Derived:: *)() to Base *(Base:: *)(), you would violate type safety:

Derived *(Derived:: *derived_method)()= &Derived::returnDerivedBAD;
Base *(Base:: *base_method)()= derived_method;
Base b;
(b.*base_method)(); // would compile, but invalid at runtime! Oops!

So you can't "work around it" safely. Even if you changed the return type to match, it would still violate type safety.

Not the answer you're looking for? Browse other questions tagged or ask your own question.