42

按照这个答案,我现在想知道 lambda 生命周期的规则是什么,以及它与自动转换创建的函数指针的生命周期有何关系。关于 lambda 的生命周期有几个问题(例如herehere),在这种情况下,答案是“它们的行为与您自己编写完整的仿函数对象完全一样”,但是两者都没有解决转换为函数指针的问题,这可能是一个非常明智的特例。

我整理了这个小例子来说明我的担忧:

#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()?问题是:“函数指针指向的函数是否需要调用函子对象本身,或者在单独的函数中重新实现主体?” 任何一个都有意义,但是转换为函数指针特别需要一个无捕获的 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;
}

在这种情况下,thestatic和非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;
}

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

4

1 回答 1

23

§5.1.2/6 说:

没有 lambda 捕获的 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
}
于 2011-11-06T10:41:14.963 回答