简体   繁体   中英

Template parameter accepting pointer to class member function

#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.

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.

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)
{
    // ...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM