Why member functions can't be used as template arguments?

23.8k views Asked by At

Why member functions cannot be used as template arguments? For example, I want to do like:

struct Foo {
    void Bar() { // do something
    }
};
template <typename TOwner, void(&func)()>
void Call(TOwner *p) {
    p->func();
}
int main() {
    Foo a;
    Call<Foo, Foo::Bar>(&a);
    return 0;
}

I know that a similar thing can be done using pointers-to-member; well, it's cool enough most of the time, but I'm just curious about why pointers "should" be used.

I see no ambiguity of interpreting "p->func()" above. Why the standard prohibits us to use member functions as template arguments? Even static member functions are not allowed according to my compiler (VC++ 2013). Does anyone know the reason? Or, is there a way to do the same thing without loss of any performance due to pointer dereferencing?

Thank you.

2

There are 2 answers

6
davidhigh On

In fact, member-function pointers can be used as template arguments (just exactly as any other pointer type may be used as template parameter):

struct A
{
    int f(float x);
};

template <int (A::F*)(float)>
struct B {};

template<A *> struct C;
template<A &> struct D;

However, according to the following excerpt from the C++ standard, one cannot pass references to members.

[temp.param]

  1. A non-type template-parameter shall have one of the following (optionally cv-qualified) types:

(4.1) — integral or enumeration type,

(4.2) — pointer to object or pointer to function,

(4.3) — lvalue reference to object or lvalue reference to function,

(4.4) — pointer to member,

(4.5) — std::nullptr_t.



Next, given you managed to pass your function type somehow and want to call it inside, you encounter the same problem as if you want to store them inside a function pointer or a std::function object: namely to call you need both, the member function as well as the concrete object. Passing only the function won't suffice.

But in fact you can achieve what you want. Just bind the function to your object and pass it afterwards:

template<typename T, typename F>
void call(T&& t, F&&f)
{
    f(std::forward<T>(t));
}

struct A
{
    void foo() { std::cout<<"hello"<<std::endl; }  
};

int main()
{
    A a;
    auto f=std::bind(&A::foo, a);   //or possibly "std::ref(a)" instead of "a"
    call(3,f);
}

DEMO

6
user657267 On

They can be used as non-type parameters, but you need to use the right syntax

struct Foo {
    void Bar() { // do something
    }
};
template <typename TOwner, void(TOwner::*func)()>
void Call(TOwner *p) {
    (p->*func)();
}
int main() {
    Foo a;
    Call<Foo, &Foo::Bar>(&a);
    return 0;
}