Obtener puntero de función a lambda?


Quiero ser capaz de obtener un puntero de función a una lambda en C++.

Puedo hacer:

int (*c)(int) = [](int i) { return i; };

Y, por supuesto, lo siguiente funciona, incluso si no está creando un puntero de función.

auto a = [](int i) { return i; };

Pero lo siguiente:

auto *b = [](int i) { return i; };

Da 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 una lambda se pueda convertir en un puntero de función sin problemas, pero el compilador no puede inferir el tipo de función y crear un puntero a ella usando auto *. Especialmente cuando puede convierte implícitamente un unique, lambda type a un puntero de función:

int (*g)(int) = a;

He creado 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.

Author: oconnor0, 2016-06-08

3 answers

Esto falla:

auto *b = [](int i) { return i; };

Porque la lambda no es un puntero. auto no permite conversiones. A pesar de que la lambda es convertible en algo que es un puntero, eso no se va a hacer por ti - tienes que hacerlo tú mismo. Ya sea con un molde:

auto *c = static_cast<int(*)(int)>([](int i){return i;});

O con algo de brujería :

auto *d = +[](int i) { return i; };
 43
Author: Barry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:00:52

Especialmente cuando puede convertir implícitamente un tipo lambda único a un puntero de función:

Pero no puede convertirlo en "un puntero de función". Solo puede convertirlo en un puntero a una firma de función específica. Esto fallará:

int (*h)(float) = a;

¿Por qué falla eso? Porque no hay una conversión implícita válida de a a h aquí.

La conversión para lambdas no es magia del compilador. El estándar simplemente dice que el tipo de cierre lambda, para no capturar, lambdas no genérico, tiene un operador de conversión implícito para los punteros de función que coinciden con la firma de su sobrecarga operator(). Las reglas para inicializar int (*g)(int) desde a permiten usar conversiones implícitas, y por lo tanto el compilador invocará ese operador.

auto no permite el uso de operadores de conversión implícitos; toma el tipo tal cual (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?

 7
Author: Nicol Bolas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-08 18:25:56

El código lambda no funciona por la misma razón que esto: {[15]]}

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 implícitamente lo convierte en un puntero de función (particular), al igual que foo.

auto no hace conversión. Nunca. Auto se comporta como un parámetro 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 se puede usar para inicializar un auto* variable.

Las Lambdas no son punteros de función. Lambdas no son std::function s. Son objetos de función auto-escritos (objetos con un operator()).

Examine esto:

void (*ptr)(int) = [](auto x){std::cout << x;};
ptr(7);

Compila y funciona en gcc (no estoy seguro de si es una extensión, ahora que lo pienso). Sin embargo, ¿qué se supone que haría auto* ptr = [](auto x){std::cout << x;}?

Sin embargo, unary + es un operador que trabaja en punteros (y no les hace casi nada), pero no en foo o un lambda.

So

auto* pauto=+foo{};

Y

auto* pfun=+[](int x){};

Ambos funcionan, mágicamente.

 5
Author: Yakk - Adam Nevraumont,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-08 22:41:49