3
#include <utility>

class Base {
public:
    virtual ~Base() {}
    virtual void base() {}
};

class Derived : public Base {
public:
    virtual void derived() {}
};

template<typename... Params>
using MemberFuncPtr = void(Derived::*)(Params...);

template<typename... Params, typename... Args>
void wrapper(MemberFuncPtr<Params...> ptr, Args&&... args)
{
    Derived* d = new Derived();

    (d->*ptr)(std::forward<Args>(args)...);

    delete d;
}

int main()
{
    wrapper(&Derived::derived);
    wrapper(&Derived::base);
    return 0;
}

Trying to run this code (GCC 7.0) gives me the following error:

prog.cc: In function 'int main()':
prog.cc:33:27: error: no matching function for call to 'wrapper(void (Base::*)())'
     wrapper(&Derived::base);
                           ^
prog.cc:18:6: note: candidate: template<class ... Params, class ... Args> void wrapper(MemberFuncPtr<Params ...>, Args&& ...)
 void wrapper(MemberFuncPtr<Params...> ptr, Args&&... args)
      ^~~~~~~
prog.cc:18:6: note:   template argument deduction/substitution failed:
prog.cc:33:27: note:   mismatched types 'Derived' and 'Base'
     wrapper(&Derived::base);
                           ^

I don't really understand why method from the base class is an issue? It's also a method for derived class. I've done simple test where I assigned Derived::base to Derived::*ptr type and that worked.

2 Answers 2

3

I don't really understand why method from the base class is an issue?

It's just a matter of what the types are. The type of &Derived::derived is void (Derived::*)(), but the type of &Derived::base is void (Base::*)(). That doesn't match void (Derived::*)(Args...), so deduction fails. Template deduction doesn't allow for conversions, even if a valid one exists in this case.

I've done simple test where I assigned Derived::base to Derived::*ptr type and that worked.

This:

MemberFuncPtr<> d = &Derived::base;
wrapper(d);

works because now d is of the right type (it now does match void (Derived::*)(Args...)), and the first line is fine because it's valid to convert a pointer to member of a base class to a pointer to member of a derived class.

Sign up to request clarification or add additional context in comments.

1 Comment

What I think I should remember is that template deduction doesn't allow conversions. Thanks!
1

I don't really understand why method from the base class is an issue?

@Barry has already explained the why in his answer.
As a side note, note that you can further generalize it and get it working:

// ...

template<typename T, typename... Params>
using MemberFuncPtr = void(T::*)(Params...);

template<typename T, typename... Params, typename... Args>
void wrapper(MemberFuncPtr<T, Params...> ptr, Args&&... args)
{
    // ...
}

1 Comment

I was worried that I will be forced to cast in the call site to void(Derived::*)(). Good to know that additional parameter can handle this "different class type" issue.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.