Reputation: 505
I'm trying to implement something similar to boost::static_visitor
, have template function accepting lambda and supporting the following API:
int i1 = Apply([](int i) { return i; }); // doesn't compile
int i2 = Apply([]() { return 10; }); // ok
bool b1 = Apply([]() { return true; }); // ok
Apply([]() { return; }); // ok
The question is continuation of this topic. The implementation
template <typename Function, typename Return = std::result_of_t<Function()>,
typename = typename std::enable_if<!std::is_same<Return, void>::value>::type>
Return Apply(Function func)
{
std::cout << "invoked via Return(*)(...)" << std::endl;
return func();
}
template <typename Function, typename Return = std::result_of_t<Function()>,
typename = typename std::enable_if<std::is_same<Return, void>::value>::type>
void Apply(Function func)
{
std::cout << "invoked via void(*)(...)" << std::endl;
func();
}
works fine if lambda doesn't have parameters
#include <functional>
#include <type_traits>
#include <iostream>
template <typename Function, typename Return = std::result_of_t<Function()>, typename = typename std::enable_if<!std::is_same<Return, void>::value>::type>
Return Apply(Function func)
{
std::cout << "invoked via Return(*)(...)" << std::endl;
return func();
}
template <typename Function, typename Return = std::result_of_t<Function()>, typename = typename std::enable_if<std::is_same<Return, void>::value>::type>
void Apply(Function func)
{
std::cout << "invoked via void(*)(...)" << std::endl;
func();
}
int main()
{
int i1 = Apply([]() { return 10; });
bool b1 = Apply([]() { return true; });
Apply([]() { return; });
std::cout << i1 << " " << b1 << std::endl;
return 0;
}
But if lambda has parameters, std::result_of_t< Function() >
needs parameters list to be passed to deduce result type of lambda (e.g. std::result_of_t< Function(int) >
).
Upvotes: 2
Views: 371
Reputation: 17026
One size fits all. :-)
template <typename Function, typename... Args>
decltype(auto) Apply(Function&& func, Args&&... args) {
std::cout << "invoked" << std::endl;
return std::forward<Function>(func)(std::forward<Args>(args)...);
}
int main()
{
int i1 = Apply([]() { return 10; });
bool b1 = Apply([]( bool b) { return !b; }, true);
Apply([]() { return; });
std::cout << i1 << " " << b1 << std::endl;
return 0;
}
Or, better yet, just use std::invoke()
.
Upvotes: 5
Reputation: 12928
You can use a helper class to get the return type of the lambda.
template<typename T>
struct ReturnVal {};
template<typename ReturnType, typename Class, typename... Args>
struct ReturnVal<ReturnType(Class::*)(Args...)> {
using type = ReturnType;
};
template<typename ReturnType, typename Class, typename... Args>
struct ReturnVal<ReturnType(Class::*)(Args...) const> {
using type = ReturnType;
};
Then you can use it by passing the decltype
of the lambda
s operator()
.
template<typename Func>
void someTemplateFunction(Func func) {
using Return = typename ReturnVal<decltype(&Func::operator())>::type;
}
With this approach you get the benefit of being able to pass in a function pointer to ReturnVal
aswell. You can also use it to extract the parameter types.
Upvotes: 0