Время жизни лямбда-объектов по отношению к преобразованию указателя на функцию

После этого ответа мне теперь интересно, каковы правила для времени жизни лямбда-выражений и как они соотносятся со временем жизни указателей на функции, которые создаются автоматическим преобразованием. Есть несколько вопросов о времени жизни лямбда-выражений (например, здесь и здесь ), и в этом случае ответы будут: «они ведут себя точно так же, как вы сами написали полный объект-функтор», однако ни один адрес преобразования в указатель функции, что вполне разумно может быть особым случаем.

Я собрал этот небольшой рабочий пример, который иллюстрирует мою озабоченность:

#include 

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;
}

Хорошо ли это определено для обоих случаев? Или только для retFun1 () ? Возникает вопрос: «Требуется ли функция, на которую указывает указатель функции, для вызова самого объекта-функтора или для повторной реализации тела в отдельной функции?» Любой из них имеет смысл, но тот факт, что преобразование в указатель на функцию конкретно требует лямбда-выражения без захвата, предполагает, что на самом деле это может быть последнее.


Другими словами, я вижу по крайней мере два разумных способа, которыми компилятор может захотеть реализовать такие лямбды. Одна из возможных законных реализаций может заключаться в том, что компилятор синтезирует такой код, как:

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;
}

, и в этом случае как static , так и не static варианты retFun будут будь умницей. Если, однако, компилятор также может реализовать лямбда, например:

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 = λ //Or do the equivalent of this in the ctor
  return lambda;
}

, то retFun2 () является неопределенным поведением.

37
задан Community 23 May 2017 в 12:17
поделиться