rodrigo
rodrigo

Reputation: 98348

Template argument deduction for member function pointers

It is known that template arguments can be pointers to member functions.

So I can write:

struct Bar
{
    int fun(float x);
};

template <int (Bar::*FUN)(float)>
struct Foo
{ /*...*/ };

typedef Foo<&Bar::fun> FooBar;

But what if I want the the Bar type itself to be a template argument:

template <typename B, int (B::*FUN)(float)>
struct Foo
{ /*...*/ };

typedef Foo<Bar, &Bar::fun> FooBar;

Now, when I use it, I have to write Bar twice!

My question is: Is there a way to force the compiler to deduce the class type automatically?

The objective is for this to just work:

typedef Foo<&Bar::fun> FooBar;
typedef Foo<&Moo::fun> FooMoo;

Upvotes: 14

Views: 3864

Answers (2)

bames53
bames53

Reputation: 88155

You probably should just write the class name in there. However, if you really want to avoid that you can use the evil magic of macros. The simple version is more dangerous:

#define TT(X) decltype(X), X

template<typename T,T t>
struct Foo
{ /* ... */ };

struct Bar {
    int fun(float) {}
};

int main() {
    Foo<TT(&Bar::fun)> f;
}

This will accept any kind of non-type template parameter, and you may encounter hard-to-understand errors if the Foo implementation only works with pointers-to-member.

To make it a bit safer you need a metafunction that tells you the class name:

template<typename T> struct member_ptr_traits;

template<typename Class,typename Ret,typename... Args>
struct member_ptr_traits<Ret (Class::*)(Args...)>
{
    typedef Class class_type;
    typedef Ret return_type;
};

#define TT(X) member_ptr_traits<decltype(X)>::class_type , X

template<typename T,int (T::*FUN)(float)>
struct Foo
{ /* ... */ };

struct Bar {
    int fun(float) {}
};

int main() {
    Foo<TT(&Bar::fun)> f;
}

Also both of these use C++11 so they won't work with old compilers. This simple version can be rewritten to use the old typeof or similar compiler extensions. Rewriting the safer version requires simulating variadic templates.

Upvotes: 6

Simple answer: no there isn't.

The problem is that for typedef Foo<&Bar::fun> FooBar; to work, the template would have to have a single non-type argument, but the type of that argument would be unknown when the template is being declared, which is not valid. On the other side, type deduction is never applied to the arguments of the template (only to the arguments to function templates, but those are the arguments to the function, not the template).

Upvotes: 7

Related Questions