层次结构中的成员函数指针

时间:2010-04-08 17:51:37

标签: c++ casting member-function-pointers

我正在使用定义接口的库:

template<class desttype>
void connect(desttype* pclass, void (desttype::*pmemfun)());

我有一个小的层次结构

class base {
   void foo();
};

class derived: public base { ... };

derived的成员函数中,我想调用

connect(this, &derived::foo);

但似乎&derived::foo实际上是base的成员函数指针; gcc吐出来

error: no matching function for call to ‘connect(derived* const&, void (base::* const&)())’

我可以通过明确地将this投射到base *来解决这个问题。但为什么编译器不能将调用与desttype = base匹配(因为derived *可以隐式转换为base *)?

另外,为什么 &derived::foo不是derived的成员函数指针?

3 个答案:

答案 0 :(得分:7)

首先,当你执行&class::member时,结果的类型总是基于成员实际声明的类。这就是一元&在C ++中的工作原理。

其次,代码无法编译,因为模板参数推断失败。从第一个参数得出desttype = derived,而从第二个参数推导出desttype = base。这就是编译失败的原因。 C ++中的模板参数推导规则不考虑this可以转换为base *类型的事实。此外,人们可以争辩说,不是将this转换为base *类型,而是将&derived::foo从指针转换为基础成员转换为指向派生成员类型。这两种方法同样可行(见下文)。

第三,C ++中的成员指针遵循 contra-variance 的规则,这意味着指向基类成员的指针可以隐式转换为指向派生类成员的指针。在您的情况下,您需要做的就是通过显式指定参数来帮助编译器完成模板参数推导,并且代码应该编译

 connect<derived>(this, &derived::foo);

由于&derived::foo指针的 contra-variance ,上述内容应该编译,即使它是指向base成员的指针。或者你可以做

 connect<base>(this, &derived::foo);

由于this指针的协方差,这也应该编译。

您还可以对实际参数使用显式强制转换(正如您在问题中提到的那样)来解决演绎模糊性,但在我看来,显式指定的模板参数看起来更好。

答案 1 :(得分:0)

成员函数指针在C ++中有很多特性,各种编译器在它们的工作方式上存在不一致。 Doug Clugston的文章"Member Function Pointers and the Fastest Possible C++ Delegates"非常好地概述了它们是如何工作的(并且不起作用):

  

处理派生类时,   有一些惊喜。例如,   下面的代码将在MSVC上编译if   你保留评论的完整性:

class SomeClass {
 public: 
    virtual void some_member_func(int x, char *p) {
       printf("In SomeClass"); };
};

class DerivedClass : public SomeClass {
 public:
 // If you uncomment the next line, the code at line (*) will fail!

//    virtual void some_member_func(int x, char *p) { printf("In DerivedClass"); };

};

int main() {
    // Declare a member function pointer for SomeClass

    typedef void (SomeClass::*SomeClassMFP)(int, char*);
    SomeClassMFP my_memfunc_ptr;
    my_memfunc_ptr = &DerivedClass::some_member_func; // ---- line (*)
}
     

奇怪的是,   &DerivedClass::some_member_func是一个   类的成员函数指针   SomeClass。它不是。的成员   DerivedClass! (有些编译器表现得很好   略有不同:例如,为   数字火星C ++,   &DerivedClass::some_member_func是   在这种情况下未定义。)但是,如果   DerivedClass覆盖   some_member_func,代码不会   编译,因为   &DerivedClass::some_member_func有   现在成为成员函数指针   班级DerivedClass

答案 2 :(得分:0)

这是模板参数推导的问题,如果调用网站上没有明确说明模板参数,那么编译器就不会尝试自动转换。

根据我的经验,解决这个问题的最佳方法是为函数声明两个模板参数:

template<typename Y, typename T>
void connect(Y * pclass, void (T::*pmemfun)());

在这种情况下,编译器可以愉快地自动为您实例化

void connect<derived, base>(derived * pclass, void (base::*pmemfun)());

这个解决方案也非常安全,因为从derived *到base *的转换将在connect中完成(我假设你在调用pclass-&gt; * pmemfun())