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

我可以通过显式转换this为来解决这个问题base *;但是为什么编译器不能匹配调用desttype = base(因为derived *可以隐式转换为base *)?

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

4

3 回答 3

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

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

于 2010-04-08T18:01:06.213 回答
0

成员函数指针在 C++ 中有很多特性,并且各种编译器在它们的工作方式上存在不一致。Doug Clugston 的文章“成员函数指针和最快的 C++ 委托”很好地概述了它们的工作原理(和不工作原理):

在处理派生类时,会有一些惊喜。例如,如果您保持注释不变,下面的代码将在 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_func是 class 的成员函数指针 SomeClass。它不是成员 DerivedClass!(一些编译器的行为略有不同:例如,对于 Digital Mars C++, &DerivedClass::some_member_func在这种情况下是未定义的。)但是,如果 DerivedClass覆盖 some_member_func,代码将无法编译,因为 &DerivedClass::some_member_func现在已成为类的成员函数指针DerivedClass

于 2010-04-08T18:54:57.887 回答
0

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

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

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

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

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

该解决方案也非常安全,因为从派生 * 到基础 * 的转换将在 connect 内部完成(我假设您正在调用 pclass->*pmemfun() )

于 2010-04-08T19:21:00.040 回答