vectores tipos punteros programacion matrices funciones ejemplos con c++ c++11 lambda object-lifetime

c++ - tipos - punteros y vectores en c



Vida útil de los objetos lambda en relación con la conversión del puntero de función (1)

Siguiendo esta respuesta , ahora me pregunto cuáles son las reglas para la vida útil de las lambdas y cómo se relacionan con la duración de los punteros de funciones que se crean mediante la conversión automática. Hay varias preguntas sobre la vida útil de lambdas (por ejemplo, here y here ), en cuyo caso las respuestas son "se comportan exactamente como usted escribió el objeto de functor completo usted mismo", sin embargo, ninguno aborda la conversión al puntero de función que podría ser bastante caso especial.

Junté este pequeño ejemplo de trabajo que ilustra mi preocupación:

#include <iostream> typedef int (*func_t)(int); // first case func_t retFun1() { static auto lambda = [](int) { return 1; }; // automatically converted to func_t return lambda; } // second case func_t retFun2() { // no static auto lambda = [](int) { return 2; }; // automatically converted to func_t and // the local variable lambda reaches the end of its life return lambda; } int main() { const int a = retFun1()(0); const int b = retFun2()(0); std::cout << a << "," << b << std::endl; return 0; }

¿Está esto bien definido para ambos casos? O solo para retFun1() ? La pregunta es: "¿es la función que los punteros de la función requieren para llamar al objeto functor mismo o volver a implementar el cuerpo en una función separada?" Cualquiera de los dos tendría sentido, pero el hecho de que la conversión a puntero a función requiera específicamente una lambda sin captura sugiere que en realidad puede ser la última.

Dicho de otra manera: puedo ver al menos dos maneras sensatas en que un compilador podría implementar tales lambdas. Una posible implementación legal podría ser que un compilador sintetice código como:

func_t retFun3() { struct __voodoo_magic_lambda_implementation { int operator()(int) const { return 3; } static int plainfunction(int) { return 3; } operator func_t() const { return plainfunction; } } lambda; return lambda; }

en cuyo caso, tanto las variantes static como las no static de retFun estarían bien. Sin embargo, si también es legal que un compilador implemente el lambda como:

static int __voodoo_impl_function(int x); static struct __voodoo_maigc_impl2 { int operator()(int) const { return 4; } operator func_t() const { return __voodoo_impl_function; } } *__magic_functor_ptr; static int __voodoo_impl_function(int x) { return (*__magic_functor_ptr)(x); } func_t retFun4() { __voodoo_maigc_impl2 lambda; // non-static, local lifetime __magic_functor_ptr = &lambda; //Or do the equivalent of this in the ctor return lambda; }

entonces retFun2() es un comportamiento indefinido.


§5.1.2 / 6 dice:

El tipo de cierre para una expresión lambda sin captura lambda tiene una función pública no virtual de conversión constante no virtual para señalar a la función que tiene el mismo parámetro y tipos de retorno que el operador de llamada de función del tipo de cierre. El valor devuelto por esta función de conversión será la dirección de una función que, cuando se invoca, tiene el mismo efecto que invocar al operador de llamada de función del tipo de cierre.

Énfasis mío

En otras palabras: como es una dirección de una función y las funciones no tienen vigencia, puede llamar a esa función siempre que lo desee. Todo lo que tienes está bien definido.

Es como si hubieras hecho:

func_t retFun2() { int __lambda0(int) { return 2; } struct { int operator(int __arg0) const { return __lambda0(__arg0); } operator decltype(__lambda0)() const { return __lambda0; } } lambda; return lambda; // just the address of a regular ol'' function }