c++ - tutorial - operador lambda python
¿Obtiene el puntero de función a lambda? (3)
Quiero poder obtener un puntero a una función lambda en C ++.
Puedo hacer:
int (*c)(int) = [](int i) { return i; };
Y, por supuesto, los siguientes trabajos, incluso si no está creando un puntero a la función.
auto a = [](int i) { return i; };
Pero lo siguiente:
auto *b = [](int i) { return i; };
Otorga este error en GCC:
main.cpp: In function ''int main()'':
main.cpp:13:37: error: unable to deduce ''auto*'' from ''<lambda closure object>main()::<lambda(int)>{}''
auto *b = [](int i) { return i; };
^
main.cpp:13:37: note: mismatched types ''auto*'' and ''main()::<lambda(int)>''
Parece arbitrario que un lambda se pueda convertir a un puntero de función sin problema, pero el compilador no puede inferir el tipo de función y crear un puntero al mismo usando auto *
. Especialmente cuando puede convertir implícitamente un unique, lambda type
en un puntero de función:
int (*g)(int) = a;
Creé un pequeño banco de pruebas en http://coliru.stacked-crooked.com/a/2cbd62c8179dc61b que contiene los ejemplos anteriores. Este comportamiento es el mismo en C ++ 11 y C ++ 14.
Especialmente cuando puede convertir implícitamente un tipo lambda único en un puntero de función:
Pero no puede convertirlo en "un puntero a la función". Solo puede convertirlo en un puntero a una firma de función específica . Esto fallará:
int (*h)(float) = a;
¿Por qué eso falla? Porque no hay una conversión implícita válida de aa h
aquí.
La conversión para lambdas no es magia del compilador. El estándar simplemente dice que el tipo de cierre lambda, para lambdas no genéricas que no capturan, tiene un operador de conversión implícito para punteros de función que coinciden con la firma de su sobrecarga de operator()
. Las reglas para inicializar int (*g)(int)
desde a
permiso usando conversiones implícitas, y por lo tanto el compilador invocará a ese operador.
auto
no permite el uso de operadores de conversión implícitos; toma el tipo tal como está (eliminando referencias, por supuesto). auto*
tampoco hace conversiones implícitas. Entonces, ¿por qué invocaría una conversión implícita para un cierre lambda y no para un tipo definido por el usuario?
El código lambda no funciona por la misma razón por la que esto no funciona:
struct foo {
operator int*() const {
static int x;
return &x;
}
};
int* pint = foo{};
auto* pint2 = foo{}; // does not compile
o incluso:
template<class T>
void test(T*) {};
test(foo{});
El lambda tiene un operador que lo convierte implícitamente en un puntero de función (particular), como foo
.
auto
no hace la conversión. Nunca. Auto se comporta como un parámetro de class T
para una función de plantilla donde se deduce su tipo.
Como el tipo en el lado derecho no es un puntero, no puede usarse para inicializar una variable auto*
.
Lambdas no son indicadores de función. Lambdas no son std::function
s. Son objetos de funciones auto escritas (objetos con un operator()
).
Examina esto:
void (*ptr)(int) = [](auto x){std::cout << x;};
ptr(7);
compila y funciona en gcc (no estoy seguro si es una extensión, ahora que lo pienso). Sin embargo, ¿qué debería hacer auto* ptr = [](auto x){std::cout << x;}
?
Sin embargo, unario +
es un operador que trabaja con punteros (y no hace casi nada con ellos), pero no en foo
o lambda.
Asi que
auto* pauto=+foo{};
Y
auto* pfun=+[](int x){};
Ambos funcionan, mágicamente.
Esto falla:
auto *b = [](int i) { return i; };
porque el lambda no es un puntero. auto
no permite conversiones. Aunque la lambda es convertible en algo que es un puntero, eso no se hará por ti, tienes que hacerlo tú mismo. Ya sea con un elenco:
auto *c = static_cast<int(*)(int)>([](int i){return i;});
O con algo de brujería :
auto *d = +[](int i) { return i; };