Указатели на функции и производные классы - PullRequest
1 голос
/ 14 октября 2011

У меня проблема с g ++ и с тем, как шаблоны взаимодействуют с указателями на функции. Рассмотрим следующее объявление шаблона.

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)();
}

Думайте о T как о классе, который содержит различные функции, которые возвращают объекты типа B. Метод, на котором следует сосредоточиться выше, это testfcnBAD (), поскольку он вызывает проблему. Вот код, который использует этот шаблон.

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);
     }
};

Когда задана последняя строка runTest (), g ++ (4.5.2) задыхается. Кажется, проблема в том, что testfcnBAD () передается указатель на функцию, которая возвращает экземпляр Derived, а TestTemplate объявляет testfcnBAD () для получения указателя на функцию, которая возвращает базовый объект. Кажется, что этот код должен быть в порядке, потому что производный объект является базовым объектом, но тот факт, что Derived является подклассом Base, может быть потерян где-то вдоль линии.

Я что-то здесь упускаю, и есть ли обходной путь?

Ответы [ 2 ]

1 голос
/ 14 октября 2011

Я не эксперт в стандарте C ++, но, насколько я могу судить, ковариантные типы возвращаемых данных применяются только при переопределении функции.Вы не можете использовать ковариантные возвращаемые типы с указателем на функцию.Я бы предложил что-то вроде:

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));
};
0 голосов
/ 15 октября 2011

Учитывая ваш код, если бы вы могли преобразовать Derived *(Derived:: *)() в Base *(Base:: *)(), вы бы нарушили безопасность типов:

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

Так что вы не можете "обойти это" безопасно. Даже если вы изменили тип возвращаемого значения для соответствия, это все равно нарушит безопасность типов.

...