Cannot deduce type of indirectly called template function

Viewed 68

The following code fragment is a stripped-down version of what should become an optimization algorithm:

template<typename F>
double helper(F f, double x)
{
    return x;
}

template<typename F, typename L>
double optimize(F f, double x, L line_search)
{
    double y = line_search(f, x);
    return y;
}

int main()
{
    auto f = [](double x){ return (x - 2) * (x - 2); };
    double solution = optimize(f, -2, helper);
}

I parametrized the main function (optimize) and the helper function (helper) with a template argument F for the function they are trying to minimize. However, the code doesn't compile; the compiler cannot figure out the type of line_search:

t04.cpp: In function ‘int main()’:
t04.cpp:17:45: error: no matching function for call to ‘optimize(main()::<lambda(double)>&, int, <unresolved overloaded function type>)’
     double solution = optimize(f, -2, helper);
                                             ^
t04.cpp:8:8: note: candidate: template<class F, class L> double optimize(F, double, L)
 double optimize(F f, double x, L line_search)
        ^~~~~~~~
t04.cpp:8:8: note:   template argument deduction/substitution failed:
t04.cpp:17:45: note:   couldn't deduce template parameter ‘L’
     double solution = optimize(f, -2, helper);

(I am using gcc v7.5.0, but I don't think it matters here.) I think I understand why the compiler doesn't know the type of line_search (it doesn't look inside optimize to see how line_search is called), but I don't know how to explain it what the correct type should be. I wish I could say optimize(f, -2, helper<type of f>), but I didn't find how to do this. What do I need to change to the code?

Incidentally, if there are other, equally generic ways of designing this but that avoid the template argument deduction problem, I would be interested to learn about them.

1 Answers

You could add a default type for L which includes F:

template<typename F>
double helper(F f, double x) {
    return f(x);
}

template<typename F, typename L = double(*)(F, double)>  // <- here
double optimize(F f, double x, L line_search) {
    double y = line_search(f, x);
    return y;
}

It will then find the correct template instantiation when you do

double solution = optimize(f, -2, helper);   // helper is helper<decltype(f)>

If helper is a function template you use often, you can make it the default:

template<typename F, typename L = double(*)(F, double)>
double optimize(F f, double x, L line_search = helper<F>) {  // <- here
    double y = line_search(f, x);
    return y;
}

which makes it possible to call optimize without specifying the helper:

double solution = optimize(f, -2);   // helper<decltype(f)> is the default
Related