如何从指向成员函数的指针推断出“T”的类型?

How to deduce type of `T` from a pointer to member function?

我有一个模板,大致是这样的:

template<typename T,void (T::*F)()>
struct Foo{
    /* ... do some stuff with the member function pointer ...*/
    //... e.g.
    T foo(){
        T t;
        t.*F;
        return t;
};

它有效,但我不喜欢我必须实例化它的方式:

Foo<SomeVeryLongClassName,&SomeVeryLongClassName::AnEvenLongerMemberFunctionName> f;

有什么方法可以使模板推导出 T? 我在想一个我可以这样调用的模板方法:

getFoo(&SomeVeryLongClassName::AnEvenLongerMemberFunctionName);

或者,因为我将主要在 T 中使用 Foo,那将只是

getFoo(AnEvenLongerMemberFunctionName);

我试过了

#include <iostream>

template <typename T,void (T::*MEMFUN)()>
struct Foo{};

template <typename T,void (T::*MEMFUN)()>
Foo<typename T,typename MEMFUN> getFoo(MEMFUN f){ 
     return Foo<typename T,typename MEMFUN>(); 
}


struct Bar { void test(){ std::cout << "MUH" << std::endl;} };

int main (){ getFoo(&Bar::test); }

报错信息其实很清楚,但是我完全看不懂...

templateExample.cpp:9:28: error: wrong number of template arguments (1, should be 2)
 Foo<typename T,typename MEMFUN>
                            ^
templateExample.cpp:4:8: error: provided for ‘template<class T, void (T::* MEMFUN)()> struct Foo’
 struct Foo{
        ^
templateExample.cpp:10:7: error: invalid type in declaration before ‘(’ token
 getFoo(MEMFUN f){
       ^
templateExample.cpp:10:7: error: template declaration of ‘int getFoo’
templateExample.cpp:10:15: error: expected ‘)’ before ‘f’
 getFoo(MEMFUN f){
               ^
templateExample.cpp: In function ‘int main()’:
templateExample.cpp:20:20: error: ‘getFoo’ was not declared in this scope
   getFoo(&Bar::test);

...为什么 "wrong number of template arguments (1, should be 2)"?

如何帮助编译器在实例化 Foo 时推导出 T ? 仅使用 C++11 之前的版本可能吗?

PS:this 非常接近于被骗,但我真的需要知道 T 的类型而不仅仅是调用成员函数(例如我需要创建一个实例)。

在 C++17 中,我们有带推导类型的非类型模板参数:

template <auto> struct Foo;

template <typename T, void (T::*MF)()> struct Foo<MF> {
  // ...
};

用法:Foo<&X::f>

您也可以直接使用 template <auto X> 并在模板中继续使用 auto 或使用 decltype(X) 获取非类型参数的类型。


在 C++17 之前,您可以尝试通过一些涉及辅助 class 模板与成员函数模板和 decltype 的扭曲来执行推导。

血淋淋的细节:

如果你定义一个函数模板 template <typename T, void(T::*MF)()> Foo<T, MF> f(MF);,其中 Foo 是你的旧式 class 模板(如 template <typename T, void (T::*MF)()> class Foo;),那么你可以使用 decltype(f(&X::h)) 来推导所需的类型 Foo<X, &X::h> 而不必重复 X。代价是您要么需要在任何地方都说 decltype,要么将其包装在宏中。