2010-04-08 162 views
4

我使用定义的接口库:成员函数指针

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&)())’ 

我可以明确地铸造thisbase *解决这个问题;但为什么编译器不能匹配desttype = base的调用(因为derived *可以隐式转换为base *)?

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

+0

是否有真的很好理由使用指向成员函数的指针,而不是仅仅在基础中声明(纯?)虚函数,并在需要时调用它? – 2010-04-08 18:01:40

+0

@Jerry,你是什么意思? – 2010-04-08 18:13:06

+0

@杰里棺材:当有 - 有。调用纯虚函数的唯一方法是通过该函数的硬编码名称。当硬编码特定函数不可接受时,使用指向成员的指针。这一定是这种情况之一。 – AnT 2010-04-08 18:15:29

回答

7

首先,当你做&class::member时,结果的类型总是基于成员实际声明的类。这就是在C++中多么单一的&

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

第三,在C构件指针++服从禁忌方差,这意味着一个指向基类成员可隐式转换为一个指针指向一个派生类成员的规则。在你的情况,你需要做的是通过显式指定参数,以帮助编译通过模板参数推导得到的,和代码应编译

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

上面应该编译,因为禁忌方差&derived::foo指针,尽管它是指向base成员的指针。或者你可以做

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

这也应该编译,因为协方差指针this

你也可以对实际参数(如你在问题中提到的)进行明确的转换以获得演绎歧义,但在我看来,在这种情况下,显式指定的模板参数看起来更好。

+0

@AndreyT,谢谢,我没有想到明确指定模板参数。至于我的第二个问题,我认为我根据你的解释把它拼凑在一起,但我建议结合你的“第一”和“第三” - 我的猜测(尽管我可能是错的)是*理由*成员函数指针的类型基于它声明的类中的成员函数指针是逆变的,而不是协变的。如果'derived :: foo'在'derived'中返回一个成员函数,它将无法在'base *'上被调用。 – 2010-04-08 18:18:58

+1

有时候你只需要有人把概念上的词语解释出来,并解释他们所代表的东西......我喜欢这样做的男人!非常好的解释。 – 2010-04-09 07:41:04

0

成员函数指针在C++中有很多特质,各种编译器在工作方式上有不一致之处。道格Clugston的文章,"Member Function Pointers and the Fastest Possible C++ Delegates",是他们的工作(不工作),一个非常漂亮的概述:

与派生类打交道时

, 也有一些惊喜。例如, 下面的代码将编译上的MSVC如果你 完整保留意见:

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_funcSomeClass类的 成员函数指针。它不是 DerivedClass的成员! (一些编译器的行为 略有不同:例如,对于 数字火星C++, &DerivedClass::some_member_func是 在这种情况下,不确定的。)但是,如果 DerivedClass覆盖 some_member_func,代码将无法 编译,因为 &DerivedClass::some_member_func已 现在变成类DerivedClass的成员函数指针 !

0

这是模板参数推导的问题,如果模板参数不明确说明在调用网站,则编译器不会尝试做自动转换。

来解决这个问题的最好办法,以我的经验,正在申报两个模板参数的函数:

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

在这种情况下,编译器能够愉快地自动实例为您

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

该解决方案,而且是完全安全的,因为从转换得出*立足*将完成内部连接(在这里我假设你正在呼吁pclass - > * pmemfun())

+0

不幸的是,我不能(至少没有过分的努力)改变方法的签名。 – 2010-04-08 20:26:57

+0

在这种情况下,剩下的唯一解决方案就是显式指定模板参数,正如AndreyT所说。 – 2010-04-08 20:49:08