4

I'm writing a delegate class but it fails to take const member functions. Here is a test case :

class foo
{
    public:
   void MemberFunction()
   {
         printf("non const member function\n");
   }

   void ConstMemberFunction() const
   {
           printf("const member function\n");
   }
};

template <class C, void (C::*Function)()>
void Call(C* instance)
{
       (instance->*Function)();
}

int main (int argc, char** argv)
{
       foo bar;
       Call<foo,&foo::MemberFunction>(&bar);
       Call<foo,&foo::ConstMemberFunction>(&bar);
}

Now the compiler (visual studio 2010) gives me an error he cannot convert the const member function to a non-const function :

2>..\src\main.cpp(54): error C2440: 'specialization' : cannot convert from 'void (__cdecl foo::* )(void) const' to 'void (__cdecl foo::* const )(void)'
2>          Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
2>..\src\main.cpp(54): error C2973: 'Call' : invalid template argument 'void (__cdecl foo::* )(void) const'
2>          ..\src\main.cpp(37) : see declaration of 'Call'

ok, easy fix (I though :P ) by adding this :

template <class C, void (C::*Function)() const>
void Call(C* instance)
{
    (instance->*Function)();
}

but now the compiler is completly confused (and me with it). it looks like he now tries to use the const function for the non-const member function and the non-const function for the const member function.

2>..\src\main.cpp(53): error C2440: 'specialization' : cannot convert from 'void (__cdecl foo::* )(void)' to 'void (__cdecl foo::* const )(void) const'
2>          Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
2>..\src\main.cpp(53): error C2973: 'Call' : invalid template argument 'void (__cdecl foo::* )(void)'
2>          ..\src\main.cpp(43) : see declaration of 'Call'
2>..\src\main.cpp(53): error C2668: 'Call' : ambiguous call to overloaded function
2>          ..\src\main.cpp(43): could be 'void Call<foo,void foo::MemberFunction(void)>(C *)'
2>          with
2>          [
2>              C=foo
2>          ]
2>          ..\src\main.cpp(37): or       'void Call<foo,void foo::MemberFunction(void)>(C *)'
2>          with
2>          [
2>              C=foo
2>          ]
2>          while trying to match the argument list '(foo *)'
2>..\src\main.cpp(54): error C2440: 'specialization' : cannot convert from 'void (__cdecl foo::* )(void) const' to 'void (__cdecl foo::* const )(void)'
2>          Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
2>..\src\main.cpp(54): error C2973: 'Call' : invalid template argument 'void (__cdecl foo::* )(void) const'
2>          ..\src\main.cpp(37) : see declaration of 'Call'
2>..\src\main.cpp(54): error C2668: 'Call' : ambiguous call to overloaded function
2>          ..\src\main.cpp(43): could be 'void Call<foo,void foo::ConstMemberFunction(void) const>(C *)'
2>          with
2>          [
2>              C=foo
2>          ]
2>          ..\src\main.cpp(37): or       'void Call<foo,void foo::ConstMemberFunction(void) const>(C *)'
2>          with
2>          [
2>              C=foo
2>          ]
2>          while trying to match the argument list '(foo *)'

If I would rename the second Call function (with the const), it all works fine but I would rather use one function.

So, can anybody point me towards what I'm doing wrong and how I can make this work ?

Thx!

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
moiself
  • 55
  • 5

3 Answers3

3

I think you might be able to address this by removing the function pointer from the template type signature and instead relying on overloading:

template <class C>
    void Call(C* ptr, void (C::*function)()) {
    (ptr->*function)();
}
template <class C>
    void Call(C* ptr, void (C::*function)() const) {
    (ptr->*function)();
}

This now uses normal function overloading to select which of the two functions should be called. const member function pointers will call down to the second version, while non-const functions will call up to the first version. This also means that you don't need to explicitly provide any type information to the template function; the compiler can deduce C in both contexts.

Let me know if (1) this doesn't work or (2) this does work, but isn't what you want.

Hope this helps!

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • Thx for the answer! This does work perfectly in my test case but now my delegate class doesn't compile anymore (tries to take '&' of an l-value reference), but that's a completly different issue and nothing to do with this question :) – moiself Jun 18 '12 at 20:15
1

Use std::bind or lambdas, which VS2010 supports, and std::function and this will cease to be a problem for you.

Puppy
  • 144,682
  • 38
  • 256
  • 465
0

Your problem is that a member function pointer is a different type than a const member function pointer. I modified your Call function to this:

template< typename C, typename funcptr_t, funcptr_t ptr >
void Call( C *instance )
{
  (instance->*ptr)();
}

and now using the additional template parameter, I can call it like this:

Call<foo,void (foo::*)(),&foo::MemberFunction>(&bar);
Call<foo,void (foo::*)() const, &foo::ConstMemberFunction>(&bar);

That's a bit messy, though. The overloading solution is better! :-)

Aaron Klotz
  • 11,287
  • 1
  • 28
  • 22