C++ 指向成员函数的指针的模板类型推断

C++ 指向成员函数的指针的模板类型推断,c++,templates,pointer-to-member,C++,Templates,Pointer To Member,在下面的问题中,我有一个与Morpheus提出的问题非常相似的问题: template <typename T> class A { public: template<class Arg1> void connect(void (T::*f)(Arg1)) { //Do some stuff } template<class Arg1, class Arg2> void connect(void (T::*f)(Arg1,A

在下面的问题中,我有一个与Morpheus提出的问题非常相似的问题:

template <typename T>
class A
{
public:
  template<class Arg1>
  void connect(void (T::*f)(Arg1)) 
  {
    //Do some stuff
  }

  template<class Arg1, class Arg2>
  void connect(void (T::*f)(Arg1,Arg2)) 
  {
    //Do some stuff
  }

  void connect(void (T::*f)()) 
  {
    //Do some stuff
  }
};

class GApp
{
public:
    void foo() {}
    void foo(double d) {}
    void foo(double d1, double d2) {}
};


int main ()
{
  A<GApp> a;
  a.connect (&GApp::foo);                // foo () - OK
  a.connect<double> (&GApp::foo);        // foo (double) - FAIL
  a.connect<double,double> (&GApp::foo); // foo (double,double) - OK
}

Richard Corden提出的解决方案要求用户显式指定函数参数类型,以区分重载。但是,此解决方案似乎不适用于具有不同数量的相同类型的参数的重载

考虑以下示例(源自原始问题):

模板
甲级
{
公众:
模板
void连接(void(T::*f)(Arg1))
{
//做点什么
}
模板
void连接(void(T::*f)(Arg1,Arg2))
{
//做点什么
}
void连接(void(T::*f)()
{
//做点什么
}
};
类GApp
{
公众:
void foo(){}
void foo(双d){}
void foo(双d1,双d2){}
};
int main()
{
A A;
a、 connect(&GApp::foo);//foo()-确定
a、 connect(&GApp::foo);//foo(double)-失败
a、 connect(&GApp::foo);//foo(double,double)-OK
}
GNU G++3.4.5和MSVC 2008未编译上述代码,两者都显示类似的错误消息:

test.cpp: In function `int main()':
test.cpp:36: error: call of overloaded `connect(<unknown type>)' is ambiguous
test.cpp:7: note: candidates are: void A<T>::connect(void (T::*)(Arg1)) [with Arg1 = double, T = GApp]
test3.cpp:13: note:               void A<T>::connect(void (T::*)(Arg1, Arg2)) [with Arg1 = double, Arg2 = double, T = GApp]
test.cpp:在函数'int main()'中:
test.cpp:36:错误:重载的'connect()'调用不明确
test.cpp:7:注意:候选项是:void A::connect(void(T::*)(Arg1))[其中Arg1=double,T=GApp]
test3.cpp:13:注意:void A::connect(void(T::*)(Arg1,Arg2))[其中Arg1=double,Arg2=double,T=GApp]
我知道一些可以让它编译的解决方法,比如将指针分配给完全相同类型的变量(例如
void(GApp::*tmp)(double)=&GApp::foo;
),或者在调用connect函数时使用更明确的形式(例如
connect((void(GApp:*)(double))(&GApp::foo));

但是,我更喜欢第一种解决方案,我想知道为什么它不起作用

提前谢谢你

对于
a.connect(&GApp::foo)
foo(double)
foo(double,double)
将分别使用一个和两个模板参数匹配
connect
的重载(对于双参数版本,将推导第二个模板参数,第一个参数由您显式提供)

如果您想消除歧义,我建议您传递确切的类型,这样就不会发生意外。与其使用这些重载,为什么不使用单个重载呢

template<typename MType, typename T>
void connect(MType T::*f) 
{
  //Do some stuff
}

a.connect<void()> (&GApp::foo);
a.connect<void(double)> (&GApp::foo);
a.connect<void(double, double)> (&GApp::foo);
模板
无效连接(MType T::*f)
{
//做点什么
}
a、 连接(&GApp::foo);
a、 连接(&GApp::foo);
a、 连接(&GApp::foo);

最后一次调用
connect
在您的代码中也应该可以正常工作。然后,您可以使用单独的模板在
connect
中解析类型
MType
,以获取参数和返回类型。

原始代码的问题是您显式定义了错误的部分。当您指定模板参数列表时例如,即使指定了一个变量,编译器也会尝试推导第二个变量,然后它又重新运行到模糊状态,因为它不知道是要使用(double,double)还是(double)变量

与其定义模板参数,不如让编译器推断它们,但显式指定要使用哪一个foo()函数:

int main()
{
    A<GApp> a;
    a.connect (&GApp::foo);                                     // foo () - OK
    a.connect( ( void (GApp::*)(double) )&GApp::foo);           // foo (double) - OK
    a.connect( ( void (GApp::*)(double, double) ) &GApp::foo);  // foo (double,double) - OK
}
intmain()
{
A A;
a、 connect(&GApp::foo);//foo()-确定
a、 connect((void(GApp::*)(double))&GApp::foo);//foo(double)-OK
a、 connect((void(GApp::*)(double,double))&GApp::foo);//foo(double,double)-OK
}

事实上,我担心第一个没有失败。很容易忘记模板参数,并以静默方式选择错误的重载。在main()中,只有第二个调用“a.connect(&GApp::foo)”没有编译。为什么您提到第三次调用也失败了?@iammilind,很抱歉,我只是误解了错误消息。我编辑了这个问题。谢谢。很好!事实上,我只是很难理解这个模板定义,甚至不知道可以定义这样的函数指针(
MType T::f*
)但是,当使用这种方法时,编译器为什么不能推断函数指针类型,即使没有过载也可以推断出。@ NcCurl,它应该能够推断出。我认为MSVC++可能会有问题。那就是编译器错误。它肯定是有效的C++。除此之外,只有GNU GCC 3.4.5无法推断。@NCrawl显然,msvc9和msvc10都无法编译litb的示例代码(当然是在更正后)。