C++ template deduction is not working

693 views Asked by At

For the following code:

#include<functional>
template<typename T>
void f(std::function<void(T)> g) {
}

template<typename T>
void g(T x) {
}

int main() {
   f(&g<int>);
}

C++14 compiler produces the error:

 no matching function for call to 'f(<unresolved overloaded function type>)'
     f(&g<int>);

I am curious why is template argument deduction not working here. It seems that, given that the argument of g is of type int, we can deduce that the argument of f is of type std::function<void(int)>, and, therefore T = int in f. Why does this not happen? I am interested in the relevant section of the C++ standard which explains this. Does T occur in a non-deduced context here?

The following similar code compiles:

#include<vector>
template<typename T>
void f(std::vector<T> vec) { 
}


int main() {
    f(std::vector<int>{});
}

So it's not the angle brackets which create a non-deduced context.

2

There are 2 answers

3
Praetorian On BEST ANSWER

Your function expects an argument of type std::function, but you're passing it a pointer to function instead. &g<int> is convertible to the std::function parameter type, but template argument deduction requires exact matches (except for the adjustments allowed by [temp.deduct.call]/2,3,4), user defined conversions are not considered.

Writing f(std::function<void(int)>(g<int>)) will work because you're now passing an std::function, so template argument deduction will succeed.

f<int>(&g<int>) also works because you've now explicitly specified T, it no longer participates in template argument deduction and user defined conversions will be attempted.

0
celtschk On

The type of &g<int> is void(*)(int). Therefore the compiler tries to generate a function with signature void f<>(void(*)(int)) which it cannot do from your template. The type std::function<void(int)> is a completely different type.

In your similar code, the object std::vector<int>{}is of type std::vector<int>, therefore the compiler tries to generate a function void f<>(std::vector<int>) which it can do from the supplied template by deducing Tto be int.

When specifying f<int>, the compiler has no need to deduce the type, and therefore cannot fail to do so. Moreover, while in deduced context no implicit conversions are considered, they are considered in non-deduced context. So by providing the type explicitly and thus making the function argument a non-deduced context, you allow the compiler to use implicit conversion to initialize the std::function<void(int)> argument with g<int>.