将lambda函数强制转换为函数指针是否安全?

时间:2013-03-27 10:48:09

标签: c++ c++11 lambda function-pointers

我有这段代码:

void foo(void (*bar)()) {
    bar();
}

int main() {
    foo([] {
        int x = 2;
    });
}

但是,我担心这会遭受同样的命运:

struct X { int i; };

void foo(X* x) {
    x->i = 2;
}

int main() {
    foo(&X());
}

它采用局部变量的地址。

第一个例子是否完全安全?

3 个答案:

答案 0 :(得分:20)

捕获 nothing 的lambda可以隐式转换为具有相同参数列表和返回类型的函数指针。只有无捕捉的lambdas可以做到这一点;如果它捕获任何东西,那么他们就不能。

除非您使用的VS2010没有实现标准的那部分,否则它们在编写编译器时尚不存在。

答案 1 :(得分:5)

除了 Nicol 完全正确的一般答案外,我还会对您的特殊恐惧添加一些观点:

  

然而,我担心这会遭受与......相同的命运   获取局部变量的地址。

当然可以,但是当你在foo内调用它时(这与你的struct示例完全一样),这绝对没有问题,因为周围的函数(main在这里case)定义局部变量/ lambda无论如何都将比被调用函数(foo)更长。如果你想保护局部变量或lambda指针以供以后使用,那么它只会是一个问题。所以

  

第一个例子是否完全安全?

是的,就像第二个例子一样。

答案 2 :(得分:5)

是的我相信第一个例子是安全的,无论在评估涉及无捕获lambda表达式的完整表达期间创建的所有临时工作的生命周期。

根据工作草案(n3485)5.1.2 [expr.prim.lambda] p6

  

没有lambda-capture的lambda表达式的闭包类型有一个   public非虚拟非显式const转换函数指针   具有与闭包相同的参数和返回类型的函数   type的函数调用运算符。此转换返回的值   function应该是一个函数的地址,当被调用时,它具有   与调用闭包类型的函数调用操作符的效果相同。

上面的段落没有说明在评估lambda表达式之后指针到函数的有效性到期。

例如,我希望以下内容有效:

auto L = []() {
   return [](int x, int y) { return x + y; };
};

int foo( int (*sum)(int, int) ) { return sum(3, 4); }


int main() {
  foo( L() );
}

虽然clang的实现细节肯定不是C ++(标准是)的最后一个词,但如果它让你感觉更好,那么在clang中实现它的方式是当解析lambda表达式并在语义上分析一个闭包时发明了lambda表达式的类型,并且静态函数被添加到类中,其语义类似于lambda的函数调用运算符。因此,即使'L()'返回的lambda对象的生命周期在'foo'的主体内结束,转换为指向函数的指针也会返回仍然有效的静态函数的地址。

考虑一下这个有点类似的案例:

struct B {
   static int f(int, int) { return 0; }
   typedef int (*fp_t)(int, int);
   operator fp_t() const { return &f; }
};
int main() {
  int (*fp)(int, int) = B{};
  fp(3, 4); // You would expect this to be ok.
}

我当然不是核心c ++专家,但是FWIW,这是我对标准书的解释,我觉得它是可以辩护的。

希望这有帮助。