Lambda closure lvalues can be passed as rvalue reference parameters

How and why does case 1 work ?

Invoking foo requires an instance of std::function<void()> that binds to an rvalue reference. std::function<void()> can be constructed from any callable object that is compatible with the void() signature.

Firstly, a temporary std::function<void()> object is constructed from []{}. The constructor used is #5 here, which copies the closure into the std::function instance:

template< class F >
function( F f );

Initializes the target with std::move(f). If f is a null pointer to function or null pointer to member, *this will be empty after the call.

Then, the temporary function instance is bound to the rvalue reference.


What is the state of fn1 closure after the function returned ?

Same as before, because it was copied into a std::function instance. The original closure is unaffected.


A lambda is not a std::function. The reference doesn't bind directly.

Case 1 works because lambdas are convertible to std::functions. This means that a temporary std::function is materialized by copying fn1. Said temporary is able to be bound to an rvalue reference, and so the argument matches the parameter.

And the copying is also why fn1 is entirely unaffected by anything that happens in foo.