接受指向类成员函数的指针的模板参数

Template parameter accepting pointer to class member function

本文关键字:指针 参数 函数 成员      更新时间:2023-10-16
#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;
}

尝试运行此代码(GCC 7.0)会给我以下错误:

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);
                           ^

我真的不明白为什么基类的方法是一个问题?它也是派生类的一种方法。我做了简单的测试,将Derived::base分配给Derived::*ptr类型并且有效。

我真的不明白为什么基类的方法是一个问题?

这只是类型是什么的问题。&Derived::derived的类型是void (Derived::*)(),但&Derived::base的类型是void (Base::*)()。这与void (Derived::*)(Args...)不符,所以演绎失败。模板扣除不允许转换,即使在这种情况下存在有效的转换。

我做了简单的测试,我将Derived::base分配给Derived::*ptr类型并且有效。

这:

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

工作是因为现在d的类型是正确的(它现在确实匹配void (Derived::*)(Args...)),并且第一行很好,因为它可以将指向基类成员的指针转换为指向派生类成员的指针是有效的。

我真的不明白为什么基类的方法是一个问题?

@Barry在他的回答中已经解释了原因。
作为旁注,请注意,您可以进一步概括它并使其正常工作:

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