Reputation: 5149
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)>'
Upvotes: 2
Views: 659
Reputation: 50540
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:
F
instead of a std::function
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; })
EDIT
GetFrom
can also be used directly in SFINAE expressions, as requested in the comments.
As a minimal, working example:
#include<type_traits>
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, typename E = decltype(GetFrom<F>::typeE(&F::operator()))>
std::enable_if_t<std::is_same<E, int>::value, E>
applyFunc(F f) {
return f(E{});
}
template<class F, typename E = decltype(GetFrom<F>::typeE(&F::operator()))>
std::enable_if_t<std::is_same<E, double>::value, E>
applyFunc(F f) {
return f(E{});
}
int main() {
int i = 0;
applyFunc([i](int d) mutable -> int { return d+d; });
applyFunc([i](double d) -> int { return d+d; });
// this won't compile, as expected
// applyFunc([i](char d) -> int { return d+d; });
}
Upvotes: 2