Function-function pointers in a hierarchy

I use 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 the derived member function, I want to call

 connect(this, &derived::foo); 

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

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

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

Also, why &derived::foo not a pointer to a function of the derived function?

+4
source share
3 answers

First, when you do &class::member , the type of result is always based on the class on which the member is actually declared. This is how unary & works in C ++.

Secondly, the code does not compile because the output of the template argument is not executed. From the first argument it turns out that desttype = derived , and from the second it is desttype = base . This makes compilation unsuccessful. The rules for outputting template arguments in C ++ do not take into account the fact that this can be converted to base * type. Moreover, it can be argued that instead of converting this to base * correct way would be to convert &derived::foo from a pointer to a base element to a pointer-receiver type. Both approaches are equally viable (see below).

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

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

The above should compile due to the inconsistency of the &derived::foo pointer, although this is a pointer to a base member. Alternatively you can do

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

This should also compile due to covariance of the this pointer.

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

+7
source

Function function pointers have many features in C ++, and different compilers have inconsistencies in how they work. Doug Clugston's article, "Member Function Pointers and C ++ Fastest Delegates," provides a very good overview of how they work (and not work):

when working with derived classes, there are some surprises. For example, the code below will compile on MSVC if you leave 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, &DerivedClass::some_member_func is a pointer to a member function of the SomeClass class. He is not a member of DerivedClass ! (Some compilers behave somewhat differently: for example, for Digital Mars C ++, &DerivedClass::some_member_func undefined in this case.) But, if DerivedClass cancels some_member_func , the code will not compile because &DerivedClass::some_member_func has now become a function pointer member of the DerivedClass class!

0
source

This is the problem of outputting the template argument, if the template arguments are not explicitly specified on the call site, then the compiler will not try to perform the 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 create for you

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

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

0
source

Source: https://habr.com/ru/post/1306388/


All Articles