简体   繁体   中英

Template type deduction for a pointer to member function

I have a very similar problem to that presented by Morpheus, in the following question:

Overloaded member function pointer to template

The solution proposed by Richard Corden requires the user to explicitly specify the function parameter type, to distinguish it among the overloads. However, this solution does not seem to work with overloads with a varying number of arguments of the same type .

Consider this example (derived from the original question):

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
}

GNU G++ 3.4.5 and MSVC 2008 do not compile the code above, both presenting similar error messages:

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]

I'm aware of some workarounds that can make it compile, like assigning the pointer to a variable of the exact same type (eg void (GApp::*tmp)(double) = &GApp::foo; ) or, using a more explicity form when calling the connect function (eg connect((void (GApp::*)(double))(&GApp::foo)); ).

However, I prefer the first solution and would like to know why it is not working.

Thank you in advance!

For a.connect<double> (&GApp::foo) , both the foo(double) and foo(double, double) will match the overload of connect with one, and with two template parameters respectively (in the case of the two parameter version, the second template argument will be deduced, the first argument was provided by you explicitly).

If you want to disambiguate ambiguous cases, I recommend that you pass the exact type, so no surprises can happen. Instead of those overloads, why not have a single overload

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

The last call to connect should work fine in your code too. You then can dissect the type MType in connect with separate templates, to get the parameter and return types.

The problem with the original code is that you are explicitly defining the wrong part. When you specify a list of template parameters, even when you specify one, the compiler will attempt to deduce the second one and then it runs right back into ambiguity because it doesn't know if you want to use (double,double) or (double) variation.

Instead of defining template parameters, let the compiler deduce them but explicitly specify which one of the foo() functions you want to use:

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
}

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