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.