Подтвердить что ты не робот

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

Следуя этому ответу Теперь мне интересно, что такое правила для жизни lambdas и как связаны с временем жизни указателей функций, которые создаются путем автоматического преобразования, Существует несколько вопросов о времени жизни лямбда (например, здесь и здесь), и в этом случае ответы "они ведут себя точно так же, как вы сами написали весь объект-функтор", однако ни одно из них не обращается к указателю на функцию, что вполне разумно может быть частным случаем.

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

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

Хорошо ли это определено для обоих случаев? Или только для 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 и non 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 = &lambda; //Or do the equivalent of this in the ctor
  return lambda;
}

то retFun2() - поведение undefined.

4b9b3361

Ответ 1

В §5.1.2/6 говорится:

Тип замыкания для лямбда-выражения без лямбда-захвата имеет публичную не виртуальную неявную функцию преобразования const, чтобы указатель на функцию, имеющую тот же параметр и возвращаемые типы, что и оператор вызова функции замыкания. Значение, возвращаемое этой функцией преобразования, должно быть адресом функции, которая при вызове имеет тот же эффект, что и при вызове оператора вызова функции закрытия.

Акцент на мой.

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

Это немного похоже на то, что вы сделали:

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
}