C++ 指向函数和派生类的指针

C++ 指向函数和派生类的指针,c++,function-pointers,derived-class,C++,Function Pointers,Derived Class,我对g++以及模板如何与函数指针交互有问题。考虑下面的模板声明。< /P> 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 <cla

我对g++以及模板如何与函数指针交互有问题。考虑下面的模板声明。< /P>
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)();
}
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);
     }
};
template类TestTemplate{
私人:
T*上下文;
公众:
测试模板(T*usingClass);
B*testfcnOK(B*arg);
B*testfcnBAD(B*(T::*fcn)(无效));
};
模板TestTemplate::TestTemplate(T*usingClass){
上下文=使用类;
}
模板B*TestTemplate::testfcnOK(B*arg){
返回arg;
}
模板B*TestTemplate::testfcnBAD(B*(T::*fcn)(void)){
返回(上下文->*fcn)();
}
将T视为一个包含各种函数的类,这些函数返回类型为B的对象。上面要关注的方法是testfcnBAD(),因为它会导致问题。下面是使用此模板的代码

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)();
}
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);
     }
};
类基{};
派生类:公共基{};
类测试员{
公众:
TestTemplate*templateClass;
Base*returnBase(){return new Base();}
Base*returnDerivedOK(){return new Derived();}
派生的*returnDerivedBAD(){return new Derived();}
void runTest()
{
templateClass=新的TestTemplate(此);
//这些工作。
Base*baseResult=templateClass->testfcnOK(newbase());
baseResult=templateClass->testfcnOK(新派生的());
baseResult=templateClass->testfcnBAD(&Tester::returnBase);
派生的*derivedResult=(派生的*)templateClass->testfcnBAD(&Tester::returnDerivedOK);
//这是行不通的。
derivedResult=(派生*)templateClass->testfcnBAD(&Tester::returnDerivedBAD);
}
};
当给出runTest()的最后一行时,g++(4.5.2)阻塞。问题似乎是testfcnBAD()被传递一个指向返回派生实例的函数的指针,而TestTemplate声明testfcnBAD()以获取指向返回基对象的函数的指针。看起来这段代码应该是可以的,因为派生对象是基对象,但是派生对象是基的子类这一事实可能会丢失


<>我在这里漏掉了什么东西,有没有工作?

< p>我不是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));
};
template类TestTemplate{
私人:
T*上下文;
公众:
测试模板(T*usingClass);
B*testfcnOK(B*arg);
模板B*testfcnBAD(D*(T::*fcn)(void));
};

> > P>我不是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));
};
template类TestTemplate{
私人:
T*上下文;
公众:
测试模板(T*usingClass);
B*testfcnOK(B*arg);
模板B*testfcnBAD(D*(T::*fcn)(void));
};

给定您的代码,如果您可以将
派生*(派生::*)()
转换为
基*(基::*)()
,您将违反类型安全:

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

所以你不能安全地“绕过它”。即使您将返回类型更改为匹配,它仍然会违反类型安全性。

给定您的代码,如果您可以将
派生*(派生:*)()
转换为
基*(基:*)()
,您将违反类型安全性:

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

所以你不能安全地“绕过它”。即使您将返回类型更改为匹配,它仍然会违反类型安全。

您所说的“阻塞”到底是什么意思?无效转换(派生*)在使用该值之前不应抛出任何错误。这是编译器错误吗?运行时异常?我们需要更多的信息。您是使用RTTI编译的吗?以下是g++输出:test.cpp:在成员函数“void Tester::runTest()”:test.cpp:32:83:错误:没有匹配的函数用于调用“TestTemplate::testfcnBAD(Derived*(Tester::*)()”test.h:22:31:注意:候选函数是:B*TestTemplate::testfcnBAD(B*(T:*)())[带T=Tester,B=Base]我删除了标题和标记中对模板的引用,因为它们与您的问题无关(这与从
派生(Tester:*)()
)初始化
基(Tester:*)()
)相关)。您所说的“阻塞”到底是什么意思?无效转换(派生*)在使用该值之前不应抛出任何错误。这是编译器错误吗?运行时异常?我们需要更多的信息。您是使用RTTI编译的吗?以下是g++输出:test.cpp:在成员函数“void Tester::runTest()”:test.cpp:32:83:错误:没有匹配的函数用于调用“TestTemplate::testfcnBAD(Derived*(Tester::*)()”test.h:22:31:注意:候选函数是:B*TestTemplate::testfcnBAD(B*(T:*)())[带T=Tester,B=Base]我删除了标题和标记中对模板的引用,因为它们与您的问题无关(这与从
派生(Tester:*)()
)初始化
基(Tester:*)()
)有关)。这似乎是可行的,尽管我仍然不知道为什么需要这样做。它还削弱了类型检查,这太糟糕了。谢谢。@Randall“您不能将协变返回类型与函数指针一起使用。”这似乎是可行的,尽管这对我来说仍然是一个谜,为什么这是必要的。它还削弱了类型检查,这太糟糕了。谢谢。@Randall“您不能将协变返回类型与函数指针一起使用。”