You can assign a lambda to a function object of type std::function<R(ArgTypes...)>
when the lambda is Lvalue-Callable for that signature. In turn, Lvalue-Callable is defined in terms of the INVOKE operation, which here means that the lambda has to be callable when it is an lvalue and all its arguments are values of the required types and value categories (as if each argument was the result of calling a nullary function with that argument type as the return type in its signature).
That is, if you give your lambda an id so that we can refer to its type,
auto l = [](int i) -> int { cout<<i<<endl; return i; };
To assign it to a function<void(const int&)>
, the expression
static_cast<void>(std::declval<decltype(l)&>()(std::declval<const int&>()))
must be well-formed.
The result of std::declval<const int&>()
is an lvalue reference to const int
, but there's no problem binding that to the int
argument, since this is just the lvalue-to-rvalue conversion, which is considered an exact match for the purposes of overload resolution:
return l(static_cast<int const&>(int{}));
As you've observed, return values are discarded if the function object signature has return type void
; otherwise, the return types have to be implicitly convertible. As Jonathan Wakely points out, C++11 had unsatisfactory behavior on this (Using `std::function<void(...)>` to call non-void function; Is it illegal to invoke a std::function<void(Args...)> under the standard?), but it's been fixed since, in LWG 2420; the resolution was applied as a post-publication fix to C++14. Most modern C++ compiler will provide the C++14 behavior (as amended) as an extension, even in C++11 mode.