tags:

views:

245

answers:

2

Do I have to explicitly instantiate a function template's type when it comes to reference type deduction. If that is the case, where is the ambiguity? Let's compare following 2 code snippets:

1st: link for the code

template <typename T> 
void foo(T& var, void(*func)(T&)) // T must be instantiated with int and it does .
{
 ++var;
} 
void ret(int & var){}
int main()
{int k =7; 
foo(k, &ret);
cout<<k;//prints 8
}

Now let's remove &'s in foo()'s decleration and we have an error.

2nd: link for the code

template <typename T> 
void foo(T var, void(*func)(T)) // T must be instantiated with int& but it doesn't.
{
 ++var;
} 

void ret(int & var){}

int main()
{int k =7; 

foo(k, &ret); //error: no matching function for call to 'foo(int&, void (*)(int&))'
cout<<k;
}

However, If I call foo by explicitly instantiating with <int&> "foo<int&>(k,&ret);", the code gives the same output as the former one. What is the reason of this error? Where is the ambiguity?

Thanks.

+1  A: 

Second version of the answer, just misinterpreted the question:

The problem with the second version is that the compiler just can't know if you want to pass by reference or pass by value.

The calling syntax for both is exactly the same, but you obviously want to pass by reference - else the increment doesn't make any sense.

Here an example to show there is no difference how you call an function with a pass by reference and one with a pass by value argument:

void Incr1(int &value) { value++; }
void Incr2(int value) { value++ } //Completely useless but for demonstration

//Now the calling of both functions
int x = 1;
Incr1(x);
Incr2(x);

If you use a pointer instead of a reference, the compiler knows what to do because you explicitly tell it that you pass a pointer to int.

#include <iostream>

template <typename T> 
void foo(T var, void(*func)(T))
{
    ++(*var);
} 

void ret(int *var){}

int main()
{
    int k =7; 

    foo(&k, &ret); 
    std::cout<<k;
}


This will make it compile but there is not much sense in it:

The error is that your function still takes a T& and not a T.

So you just need this little modification:

template <typename T> 
void foo(T var, void(*func)(T&))
{
 ++var;
} 

void ret(int & var){}

int main()
{
    int k =7; 

    foo(k, &ret);
    std::cout<<k;
}
Fionn
@Fionn - could you clarify your answer? I know you got it right: the ret() function and the function pointer in the template must have the exactly same signatures. But it's not that obvious from the text of your response.
Arkadiy
Comptrol
Now i understand what you are trying to do. The problem is without an explicit reference the compiler just can't know if you want to pass by value or reference. It can only pass by reference if it is asked to pass by reference.
Fionn
+1  A: 

As highlighted in Fionn's answer, the problem is that the compiler deduces two distinct types for T, int and int&. 18.8.2.4/2 has the following:

In some cases, the deduction is done using a single set of types P and A, in other cases, there will be a set of corresponding types P and A. Type deduction is done independently for each P/A pair, and the deduced template argument values are then combined. If type deduction cannot be done for any P/A pair, or if for any pair the deduction leads to more than one possible set of deduced values, or if different pairs yield different deduced values, or if any template argument remains neither deduced nor explicitly specified, template argument deduction fails.

The highlighted text I believe covers your example. You didn't ask, but one option you potentially have is to use two template parameters in your example. Both of these cases can be deduced, so you could then use some other template trickery possibly via enable if to create a new type which represents the version you want, ie. with reference or without.

Richard Corden
Comptrol
Comptrol
Richard Corden