Why cannot use `decltype()` for `std::function<>` with lambda?

Viewed 207

we know that decltype() could be used to get type of variables, just like following:

int a = 0;
using a_t = decltype(a);
a_t b = -1; // it worked, and type of b is int

but it didn't work for this:

auto f = [](int a) -> int { return a + 1;}; // the type of callable should be int(int)?

std::function<decltype(f)> F(f); // error
std::function<int(int)> G(f);    // worked

why? and is there any method to get function type(in the <>) of lambda expression?

2 Answers

This is simply improper usage of the std::function wrapper. This type is meant to hide the actual implementation by wrapping it into a type-erased object with as little information about the underlying callable as possible: and this is the function signature.

When you use decltype(f), you get the acutal unique, compiler-generated type of the lambda expression. But this is not how you can instantiate a std::function, as the basic, non-specialized template template<class> std::function is undefined. Only the specialization template<class R, class ...Args> std::function<R(Args...)> is defined, and you cannot instantiate this with the type of a lambda expression.

Do note that all lambda expression have a signature, too: they accept some arguments (possibly template parameter types) and return a value of some type. Those is the information you need to put into the std::function instantiation. In your case int(int) as you posted yourself.

Is there any method to get function type (in the <>) of lambda expression?

Yes, for capture-less non-generic lambdas there is a user-defined conversion function to get the equivalent function pointer type. Here's how you can use that:

#include <functional>
#include <type_traits>

auto f = [](int a) -> int { return a + 1;};

template <class F>
using fn_type_t = std::remove_pointer_t<decltype(+std::declval<F>())>;

std::function<fn_type_t<decltype(f)>> F(f);

Godbolt.org

The unary + operator triggers the conversion to int(*)(int), and then std::remove_pointer obtains int(int) from the type of that expression.

Related