Descubrí que los lvalue
cierres lambda siempre se pueden pasar como rvalue
parámetros de función.
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 un 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 de fn1
cierre después de que la función regresó?
fn1
se convierte implícitamente a unastd::function
enfoo(fn1)
. Esa función temporal es entonces un valor r.std::function
pueden deducirse los argumentos de plantilla de una lambda?". Su programa no intenta deducir los argumentos de la plantillastd::function
, por lo que no hay problema con la conversión implícita.std::function
tiene un constructor no explícito que acepta cierres lambda, por lo que hay una conversión implícita. Pero en las circunstancias de la pregunta vinculada, la instanciación de plantilla destd::function
no se puede inferir del tipo lambda. (Por ejemplo,std::function<void()>
se puede construir[](){return 5;}
a pesar de que tiene un tipo de retorno no nulo.Respuestas:
La invocación
foo
requiere una instanciastd::function<void()>
que se una a una referencia de valor .std::function<void()>
se puede construir a partir de cualquier objeto invocable que sea compatible con lavoid()
firma.En primer lugar,
std::function<void()>
se construye un objeto temporal[]{}
. El constructor utilizado es el # 5 aquí , que copia el cierre en lastd::function
instancia:Entonces, la
function
instancia temporal está vinculada a la referencia rvalue.Igual que antes, porque se copió en una
std::function
instancia. El cierre original no se ve afectado.fuente
Una lambda no es un
std::function
. La referencia no se une directamente .El caso 1 funciona porque las lambdas son convertibles a
std::function
s. Esto significa que unstd::function
material temporal se materializa copiandofn1
. 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 el motivo por el cual no
fn1
se ve afectado por nada de lo que sucedefoo
.fuente
fn1
no tiene estado, ya que no captura nada.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 el lambda es invocable por los argumentos de esto
std::function
, es implícitamente convertible a él a través del constructor de conversión de plantillas destd::function
. El resultado de la conversión es un prvalue y, por lo tanto, puede vincularse con la referencia rvalue.fuente