Search code examples
c++templatestemplate-argument-deduction

How to deduce type of `T` from a pointer to member function?


I have a template, more or less like this:

template<typename T,void (T::*F)()>
struct Foo{
    /* ... do some stuff with the member function pointer ...*/
    //... e.g.
    T foo(){
        T t;
        t.*F;
        return t;
};

it works, but I dont like the way I have to instantiate it:

Foo<SomeVeryLongClassName,&SomeVeryLongClassName::AnEvenLongerMemberFunctionName> f;

Is there some way I can make the template deduce T? I was thinking of a template method that I could call like this:

getFoo(&SomeVeryLongClassName::AnEvenLongerMemberFunctionName);

or, as I will mainly use Foo inside T, that would be just

getFoo(AnEvenLongerMemberFunctionName);

I tried this

#include <iostream>

template <typename T,void (T::*MEMFUN)()>
struct Foo{};

template <typename T,void (T::*MEMFUN)()>
Foo<typename T,typename MEMFUN> getFoo(MEMFUN f){ 
     return Foo<typename T,typename MEMFUN>(); 
}


struct Bar { void test(){ std::cout << "MUH" << std::endl;} };

int main (){ getFoo(&Bar::test); }

The error messages are actually quite clear, but I dont understand them at all...

templateExample.cpp:9:28: error: wrong number of template arguments (1, should be 2)
 Foo<typename T,typename MEMFUN>
                            ^
templateExample.cpp:4:8: error: provided for ‘template<class T, void (T::* MEMFUN)()> struct Foo’
 struct Foo{
        ^
templateExample.cpp:10:7: error: invalid type in declaration before ‘(’ token
 getFoo(MEMFUN f){
       ^
templateExample.cpp:10:7: error: template declaration of ‘int getFoo’
templateExample.cpp:10:15: error: expected ‘)’ before ‘f’
 getFoo(MEMFUN f){
               ^
templateExample.cpp: In function ‘int main()’:
templateExample.cpp:20:20: error: ‘getFoo’ was not declared in this scope
   getFoo(&Bar::test);

...why "wrong number of template arguments (1, should be 2)" ?

How can I help the compiler to deduce T when instantiating a Foo ? Is it possible with only pre-C++11?

PS: this is very close to being a dupe, but I really need to know the type of T and not just call the member function (e.g. I need to create an instance).


Solution

  • In C++17 we have non-type template parameters with deduced types:

    template <auto> struct Foo;
    
    template <typename T, void (T::*MF)()> struct Foo<MF> {
      // ...
    };
    

    Usage: Foo<&X::f>

    You can also directly use template <auto X> and either keep using auto inside your template or use decltype(X) to get at the type of the non-type parameter.


    Prior to C++17, you could try to perform deduction via some contortions involving helper class templates with member function templates and decltype.

    The gory details:

    If you define a function template template <typename T, void(T::*MF)()> Foo<T, MF> f(MF);, where Foo is your old-style class template (like template <typename T, void (T::*MF)()> class Foo;), then you can use decltype(f(&X::h)) to deduce the desired type Foo<X, &X::h> without having to repeat X. The price is that you either need to say decltype everywhere, or you wrap that in a macro.