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

发布于 2024-08-28 12:37:11 字数 835 浏览 6 评论 0原文

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

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

并且我有一个小的层次结构

class base {
   void foo();
};

class derived: public base { ... };

衍生的成员函数中,我想调用

connect(this, &derived::foo);

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

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

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

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

I'm using a library that defines an interface:

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

and I have a small hierarchy

class base {
   void foo();
};

class derived: public base { ... };

In a member function of derived, I want to call

connect(this, &derived::foo);

but it seems that &derived::foo is actually a member function pointer of base; gcc spits out

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

I can get around this by explicitly casting this to base *; but why can't the compiler match the call with desttype = base (since derived * can be implicitly cast to base *)?

Also, why is &derived::foo not a member function pointer of derived?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

许你一世情深 2024-09-04 12:37:11

首先,当您执行 &class::member 时,结果的类型始终基于实际声明成员的类。这就是一元 & 的工作方式C++。

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

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

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

上面应该编译,因为 contra-variance &衍生::foo 指针,即使它是指向 base 成员的指针。或者,您可以这样做,

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

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

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

Firstly, when you do &class::member the type of the result is always based on the class that member actually declared in. That's just how unary & works in C++.

Secondly, the code does not compile because the template argument deduction fails. From the first argument it derives that desttype = derived, while from the second one it derives that desttype = base. This is what makes the compilation to fail. The template argument deduction rules in C++ don't consider the fact that this can be converted to base * type. Moreover, one can argue that instead of converting this to base * type, the proper way would be to convert &derived::foo from pointer-to-base-member to pointer-to-derived-member type. Both approaches are equally viable (see below).

Thirdly, member pointers in C++ obey the rules of contra-variance, which means that a pointer to a base class member can be implicitly converted to a pointer to a derived class member. In your case, all you need to do is to help the compiler get through template argument deduction by specifying the argument explicitly, and the code should compile

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

The above should compile because of contra-variance of &derived::foo pointer, even though it is a pointer to base member. Alternatively you can do

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

This should also compile because of covariance of this pointer.

You can also use explicit casts on the actual arguments (as you mention in the question) to get through the deduction ambiguity, but in my opinion in this case the explicitly specified template argument looks better.

凉薄对峙 2024-09-04 12:37:11

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

在处理派生类时,
有一些惊喜。例如,
下面的代码将在 MSVC 上编译,如果
您保留评论不变:

class SomeClass {
 民众: 
    虚拟无效 some_member_func(int x, char *p) {
       printf("在某个类中"); };
};

类 DerivedClass : 公共 SomeClass {
 民众:
 // 如果取消注释下一行,则 (*) 行的代码将失败!

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

};

int main() {
    // 声明 SomeClass 的成员函数指针

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

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

Member function pointers have a lot of idiosyncrasies in C++, and various compilers have inconsistencies in how they work. Doug Clugston's article, "Member Function Pointers and the Fastest Possible C++ Delegates", is a very nice overview of how they work (and don't work):

when dealing with derived classes,
there are some surprises. For example,
the code below will compile on MSVC if
you leave the comments intact:

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

Curiously enough,
&DerivedClass::some_member_func is a
member function pointer of class
SomeClass. It is not a member of
DerivedClass! (Some compilers behave
slightly differently: e.g., for
Digital Mars C++,
&DerivedClass::some_member_func is
undefined in this case.) But, if
DerivedClass overrides
some_member_func, the code won't
compile, because
&DerivedClass::some_member_func has
now become a member function pointer
of class DerivedClass!

長街聽風 2024-09-04 12:37:11

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

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

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

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

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

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

This is a problem of template argument deduction, if the template arguments are not explicitly stated on the invocation site, then the compiler will not try to do automatic conversion.

The best way to get around this, in my experience, is to declare two template arguments for the function:

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

In this case, the compiler can happily automatically instantiate for you

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

This solution is also perfectly safe, since the conversion from derived * to base * will be done inside connect (where I assume you are calling pclass->*pmemfun() )

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文