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

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

He armado 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á bien definido para ambos casos? O solo pararetFun1()? La pregunta es: "¿es la función a la que apunta el puntero de la función para llamar al objeto functor en sí mismo o para 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 al puntero de función requiera específicamente un lambda sin captura sugiere que en realidad puede ser el último.

Ponga otra forma: puedo ver al menos dos formas sensatas en que un compilador podría querer 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;
}

n cuyo caso tanto lastatic y nostatic variantes deretFun estaría bien. Sin embargo, si también es legal que un compilador implemente la 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;
}

luegoretFun2() es un comportamiento indefinido.

Respuestas a la pregunta(1)

Su respuesta a la pregunta