如何从指向成员函数的指针推导出`T`的类型?

时间:2017-04-04 15:40:54

标签: c++ templates template-deduction

我有一个模板,或多或少像这样:

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

或者,因为我将主要使用Foo内的T,这只是

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

...为什么“错误的模板参数数量(1,应该是2)”?

如何在实例化T时帮助编译器推断Foo? 只有pre-C ++ 11才有可能吗?

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

1 个答案:

答案 0 :(得分:4)

在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之前,您可以尝试通过一些涉及成员函数模板和decltype的辅助类模板的扭曲来执行演绎。

血淋淋的细节:

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