views:

67

answers:

5

If you have this generic function:

template<class type, class ret, class atype1, class atype2, class atype3>
ret call3(type *pClass, ret(type::* funcptr)(atype1, atype2, atype3), atype1 arg, atype2 arg2, atype3 arg3)
{
    //do some stuff here
    return (pClass->*funcptr)(arg, arg2, arg3);
}

and you do this:

class MyClass
{
  public: 
    void test(int, int, int){};
    void test(int, int){};
    void test(int, int, const char *){};  //comment this line, and it works fine.
};

...

    MyClass *a = new MyClass();
    call3(a, &MyClass::test, 1, 2, 3);

g++ will say:

no matching function for call to `call3(MyClass*&, <unknown type>, int, int, int)'

Is there any way to fix this? (My code is probably very bad also since I'm not very good at C++.)

A: 

The compiler cannot deduce the return parameter type. You'll have to specify template parameters of call3 explicitly.

Alex Emelianov
+3  A: 

You have to specify which of the three test you need. This can be accomplished with a cast:

call3(a, static_cast<void (MyClass::*)(int, int, int)>(&MyClass::test), 1, 2, 3);

It's not really all that clean. It would probably be better to just give the functions different names.

JoshD
jcao219
@jcao219: You can, but this has some complexity with respect to argument deduction. See my response
Chubsdad
JoshD
A: 

I think the problem here is that compiler is not able to resolve which test function you are referring to.

If suppose you have following tho methods

void test(int, int, int);
void test(int, int, const char *); 

now if you call test like this

test(1, 2, 3);

3 can be implicitly converted to const char *. So compiler wont be able to resolve between the two overloads of function test, and hence the compilation error.

The solution given by JoshD should solve your problem

Yogesh Arora
+3  A: 

You can explicitly specify which template to use.

call3<MyClass, void, int, int, int>( a, &MyClass::test, 1, 2, 3 );

If you rearange the order of your template parameters, you can get the MyClass and void deduced from the argument, so the call when needing an overload will look like this:

call3<int,int,int>( a, &MyClass::test, 1, 2, 3 )

Note that when you dont actually need the explicit overload resolution it can still be

call3( a, &MyClass::otherfunction, 1,2,3 );
Michael Anderson
The one where the template parameters are reordered seemed to have worked. Thank you!
jcao219
+2  A: 

The reason is that the compiler has two overloads to choose from each of which takes three parameters. Note that during argument deduction process, the compiler deduces participating template parameters from function call arguments indepdently looking at the parameter/argument pair.

Argument deduction succeeds only when the template parameters are determined unambiguously looking at each parameter/argument pair. This means, that while looking at your second parameter/argument pair, it does (and cannot) rely on the fact that atype1, atype2 and atype3 each are of type 'int', even if you rearrange the template parameters as

template<class type, class ret, class atype1, class atype2, class atype3> 
ret call3(type *pClass, atype1 arg, atype2 arg2, atype3 arg3, ret(type::* funcptr)(atype1, atype2, atype3)) 
{ 
    //do some stuff here 
    return (pClass->*funcptr)(arg, arg2, arg3); 
} 

Here's the quote from the Standard that supports this:

The non-deduced contexts are:

— The nested-name-specifier of a type that was specified using a qualified-id.

— A non-type template argument or an array bound in which a subexpression references a template parameter.

— A template parameter used in the parameter type of a function parameter that has a default argument that is being used in the call for which argument deduction is being done.

— A function parameter for which argument deduction cannot be done because the associated function argument is a function, or a set of overloaded functions (13.4), and one or more of the following apply:

more than one function matches the function parameter type (resulting in an ambiguous deduction),or

— no function matches the function parameter type, or

— the set of functions supplied as an argument contains one or more function templates.

Now for the solution,

I think the only solution is that you will have to explicitly specify the template parameters rather than relying on template argument deduction. A little rearrangement of the template parameters will help

template<class atype1, class atype2, class atype3, class type, class ret> 
ret call3(type *pClass, ret(type::* funcptr)(atype1, atype2, atype3), atype1 arg, atype2 arg2, atype3 arg3) 
{ 
    //do some stuff here 
    return (pClass->*funcptr)(arg, arg2, arg3); 
} 

int main(){
    MyClass *a = new MyClass(); 
    call3<int, int, int>(a, &MyClass::test, 1, 2, 3); 
}
Chubsdad
call3 takes five parameters. You have to specify all of them as there are no default parameters. So, add `MyClass` and `void` to that.
JoshD