与函数指针转换相关的lambda对象的生命周期

Fle*_*exo 37 c++ lambda object-lifetime c++11

根据这个答案,我现在想知道lambda的生命周期是什么规则,以及与自动转换创建的函数指针的生命周期有什么关系.关于lambda的生命周期有几个问题(例如这里这里),在这种情况下,答案是"它们的行为与你自己编写完整的仿函数对象完全一样",但是它们都没有解决转换为函数指针的问题.特殊情况.

我把这个小小的工作实例放在一起,说明了我的担忧:

#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;
}
Run Code Online (Sandbox Code Playgroud)

这两种情况都有明确定义吗?还是仅限于retFun1()?问题是:"函数指针指向的函数需要调用函子对象本身,还是在单独的函数中重新实现正文?" 任何一个都有意义,但事实上转换为函数指针特别需要一个无捕获的lambda,这表明它实际上可能是后者.


换句话说 - 我可以看到编译器可能想要实现这样的lambda的至少两种合理方式.一种可能的合法实现可能是编译器合成代码,如:

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;
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下static,非static变体和非变体retFun都可以.但是,如果编译器实现lambda也是合法的:

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;
}
Run Code Online (Sandbox Code Playgroud)

然后retFun2()是未定义的行为.

GMa*_*ckG 20

§5.1.2/ 6说:

没有lambda-capture的lambda表达式的闭包类型有一个公共的非虚拟非显式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
}
Run Code Online (Sandbox Code Playgroud)

  • @awoodland:请注意,只有没有捕获的lambdas才能转换为函数指针.这意味着编译器可以自由地将它们合成为结构而不是直接作为函数(实际上这是明智的做法,因为你不需要额外的`this`).标准散文是关于行为的,没有人说在这种情况下需要一个对象. (3认同)
  • 那么您获得的地址的函数不允许依赖于函子对象本身?例如,在我刚刚进行的编辑中,`retFun4()` 似乎满足 §5.1.2/6,因为它*“与调用闭包类型的函数调用运算符具有相同的效果”*,但它取决于lambda 对象本身也是如此。 (2认同)
  • @GMan - 对于 lambda 本身的生命周期,它与调用 lambda 具有相同的效果 (2认同)
  • 该标准仅保证“与调用闭包类型的函数调用运算符具有相同的效果”,但由于闭包对象已经被销毁,因此无法调用闭包类型的函数调用运算符。即使在闭包对象的生命周期结束后,我也没有在标准中看到函数指针有效的保证。 (2认同)