Reputation: 7435
I found that lvalue
lambda closures can always be passed as rvalue
function parameters.
See the following simple demonstration.
#include <iostream>
#include <functional>
using namespace std;
void foo(std::function<void()>&& t)
{
}
int main()
{
// Case 1: passing a `lvalue` closure
auto fn1 = []{};
foo(fn1); // works
// Case 2: passing a `lvalue` function object
std::function<void()> fn2 = []{};
foo(fn2); // compile error
return 0;
}
Case 2 is the standard behavior (I just used a std::function
for demonstration purposes, but any other type would behave the same).
How and why does case 1 work ? What is the state of fn1
closure after the function returned ?
Upvotes: 19
Views: 1246
Reputation: 238311
What is the state of fn1 closure after the function returned ?
fn1
is stateless, since it captures nothing.
How and why does case 1 work ?
It works because the argument is of different type than the type that is rvalue referenced. Because of having a different type, implicit conversions are considered. Since the lambda is Callable for the arguments of this std::function
, it is implicitly convertible to it through the template converting constructor of std::function
. The result of the conversion is a prvalue, and thus can be bound with the rvalue reference.
Upvotes: 5
Reputation: 93264
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)
. Iff
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.
Upvotes: 8
Reputation: 170065
A lambda is not a std::function
. The reference doesn't bind directly.
Case 1 works because lambdas are convertible to std::function
s. 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
.
Upvotes: 10