How to auto deduct argument and return type of lambda with std::function?

Viewed 39

This code:

template<typename Arg, typename Ret>
Ret fun(std::function<Ret(Arg)> fun){
    Arg x=0;
    return fun(x);
};

auto f=[](int x){return x;};
fun(f);  //compilation failed.

doesn't work. I want to get the argument and return type of lambda in fun.

I think the argument type has already known at comile time, why the compilier can't deduct it automaticall?

1 Answers

The problem here is that the lambda is not an std::function, so you're asking the compiler to do a deduction (find the type of Arg AND Ret) and a convertion i.e. convert the lambda to an std::function. The combination causes a conflict.

If you want to still use std::function as argument type for fun, then the easier thing to do is to make a utility that identifies what std::function to cast your callable to, e.g.:

#include <functional>

using namespace std;

template<typename T>
struct memfun_type
{
    using type = void;
};

template<typename Ret, typename Class, typename... Args>
struct memfun_type<Ret(Class::*)(Args...) const>
{
    using type = std::function<Ret(Args...)>;
};

template<typename F>
typename memfun_type<decltype(&std::decay_t<F>::operator())>::type
function_from(F&& func)
{
    return std::forward<F>(func);
}

which you'd use as

fun(function_from(f)); // Auto-detect <Ret(Args...)> types.

Demo

After showing the mechanics of how auto-detection works, note that from C++17 onwards the CTAD feature does this for you. So in newer compilers this also works:

fun(std::function(f)); // Again no types specified.

Alternatively, you can make your f a bit more generic and use just the argument deduction, like:

template <class F>
auto fun(F &&fun)
{
    int x=0;
    return std::invoke(std::forward<F>(fun), x);
};

fun(f); // Call directly with your lambda

Demo

Using c++20 concepts, this version can be restricted to the argument and input function types that you want.

Related