c++ - Los valores de cierre de Lambda se pueden pasar como parámetros de referencia de valor
closures c++14 (3)
Descubrí que los cierres
lvalue
lambda siempre se pueden pasar como parámetros de la función
rvalue
.
Vea la siguiente demostración simple.
#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;
}
El caso 2 es el comportamiento estándar (acabo de utilizar una
std::function
para fines de demostración, pero cualquier otro tipo se comportaría igual).
¿Cómo y por qué funciona el caso 1?
¿Cuál es el estado del cierre de
fn1
después de que la función regresó?
¿Cómo y por qué funciona el caso 1?
Invocar
foo
requiere una instancia de
std::function<void()>
que se une a una
referencia rvalue
.
std::function<void()>
puede construirse a partir de cualquier
objeto invocable
que sea compatible con la firma
void()
.
En primer lugar, un objeto temporal
std::function<void()>
se construye a partir de
[]{}
.
El constructor utilizado es el # 5
here
, que copia el cierre en la instancia
std::function
:
template< class F > function( F f );
Inicializa el objetivo con
std::move(f)
. Sif
es un puntero nulo para funcionar o un puntero nulo para miembro,*this
estará vacío después de la llamada.
Entonces, la instancia de
function
temporal está vinculada a la referencia rvalue.
¿Cuál es el estado del cierre de fn1 después de que la función regresó?
Igual que antes, porque se copió en una instancia
std::function
.
El cierre original no se ve afectado.
¿Cuál es el estado del cierre de fn1 después de que la función regresó?
fn1
tiene estado, ya que no captura nada.
¿Cómo y por qué funciona el caso 1?
Funciona porque el argumento es de un tipo diferente al tipo al que se hace referencia rvalue.
Debido a que tiene un tipo diferente, se consideran las conversiones implícitas.
Dado que lambda es invocable para los argumentos de esta
std::function
, es convertible implícitamente a través del constructor de conversión de plantillas de
std::function
.
El resultado de la conversión es un prvalue y, por lo tanto, puede vincularse con la referencia rvalue.
Una lambda no es una
std::function
.
La referencia no se une
directamente
.
El caso 1 funciona porque las lambdas son convertibles a
std::function
s.
Esto significa que una
std::function
temporal se materializa
copiando
fn1
.
Dicho temporal puede vincularse a una referencia de valor r, por lo que el argumento coincide con el parámetro.
Y la copia también es la razón
fn1
cual
fn1
se ve afectado por nada de lo que sucede en
foo
.