JE42 JE42 - 5 months ago 34
C++ Question

template with std::function doesn't directly match lambda

How can I best define the template function

applyFunc
to match lambda's the signature ?

I don't want to define a generic template argument capturing the function type as a whole,
since I would like to use the type
E
of the argument of the function passed in.

#include <functional>
template<class E>
int applyFunc(std::function<int(E)> f) {
return f(E{});
}

int main()
{
auto asLambda = [](int d) -> int { return d+d; };
std::function<int(int)> asFunc = asLambda;

applyFunc(asLambda); //this doesn't :(
applyFunc(asFunc); //this works
}


Compilation fails with:

15:23: error: no matching function for call to 'applyFunc(main()::<lambda(int)>&)'
15:23: note: candidate is:
6:5: note: template<class E> int applyFunc(std::function<int(E)>)
6:5: note: template argument deduction/substitution failed:
15:23: note: 'main()::<lambda(int)>' is not derived from 'std::function<int(E)>'

Answer Source

How can I best define the template function applyFunc to match lambda's the signature ?

As long as you accept to use non capturing lambdas only (as you did in the example code), you can exploit the fact that they decay to function pointers.
As a minimal, working example:

template<class E>
int applyFunc(int(*f)(E)) {
    return f(E{});   
}

int main() {
    auto asLambda = [](int d) -> int {  return d+d; };
    applyFunc(+asLambda);
}

If you want to use capturing lambdas instead, you can extract the type E somehow if you accept the followings:

  • You must use a generic type F instead of a std::function
  • You cannot use generic lambdas

Then, you can look directly at the operator() of your lambda.
It follows a minimal, working example:

template<typename F>
struct GetFrom {
    template<typename R, typename E>
    static E typeE(R(F::*)(E) const);

    // required for mutable lambdas
    template<typename R, typename E>
    static E typeE(R(F::*)(E));
};

template<class F>
int applyFunc(F f) {
    using E = decltype(GetFrom<F>::typeE(&F::operator()));
    return f(E{});   
}

int main() {
    int i = 0;
    applyFunc([i](int d) mutable -> int {  return d+d; });
    applyFunc([i](int d) -> int {  return d+d; });
}

You can easily extend it to multiple arguments if you need. Use a std::tuple as a return type and get the i-th type from it.

Finally, if you want to use capturing lambdas and assign them to a std::function for whatever reason, be aware that E cannot be automatically deduced and thus you have to explicitly specify it (as suggested in the comments to the question by @cpplearner):

applyFunc<int>([](int d) { return d+d; })