层次结构中的成员函数指针
我正在使用一个定义接口的库:
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
首先,当您执行
&class::member
时,结果的类型始终基于实际声明成员的类。这就是一元&
的工作方式C++。其次,代码无法编译,因为模板参数推导失败。从第一个参数派生出
desttype = returned
,而从第二个参数派生出desttype = base
。这就是编译失败的原因。 C++ 中的模板参数推导规则不考虑this
可以转换为base *
类型的事实。此外,有人可能会说,正确的方法是将&categories::foo
从指向基成员的指针到指向派生成员类型的指针。两种方法同样可行(见下文)。第三,C++中的成员指针遵循逆变规则,这意味着指向基类成员的指针可以隐式转换为指向派生类成员的指针。在您的情况下,您需要做的就是通过显式指定参数来帮助编译器完成模板参数推导,并且代码应该编译
上面应该编译,因为
的 contra-variance &衍生::foo
指针,即使它是指向base
成员的指针。或者,您可以这样做,由于
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 thatdesttype = base
. This is what makes the compilation to fail. The template argument deduction rules in C++ don't consider the fact thatthis
can be converted tobase *
type. Moreover, one can argue that instead of convertingthis
tobase *
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
The above should compile because of contra-variance of
&derived::foo
pointer, even though it is a pointer tobase
member. Alternatively you can doThis 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.
成员函数指针在 C++ 中有很多特性,并且不同的编译器在它们的工作方式上也不一致。 Doug Clugston 的文章“成员函数指针和最快的 C++ 委托”,很好地概述了它们如何工作(以及不工作):
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):
这是一个模板参数推导的问题,如果模板参数没有在调用站点上明确声明,那么编译器将不会尝试进行自动转换。
根据我的经验,解决这个问题的最佳方法是为函数声明两个模板参数:
在这种情况下,编译器可以很高兴地自动为您实例化
这个解决方案也是完全安全的,因为从派生 * 到基 * 的转换将在 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:
In this case, the compiler can happily automatically instantiate for you
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() )