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

Eri*_*ric 13 c++ lambda function-pointers c++11

我有这个代码:

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

int main() {
    foo([] {
        int x = 2;
    });
}
Run Code Online (Sandbox Code Playgroud)

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

struct X { int i; };

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

int main() {
    foo(&X());
}
Run Code Online (Sandbox Code Playgroud)

它取一个局部变量的地址.

第一个例子是完全安全的吗?

Nic*_*las 20

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

除非你使用VS2010,它没有实现标准的那部分,因为它们在编写编译器时还不存在.


Chr*_*ica 5

除了尼科尔完全正确的一般答案之外,我还会对你的特殊担忧添加一些看法:

但是,我担心这会遭受与...相同的命运,它会获取局部变量的地址.

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

第一个例子是完全安全的吗?

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

  • "*是的,就像第二个例子一样.*"迂腐地说,第二个例子是非法的,因为它需要一个临时的地址,因此既不安全也不安全.; - ] (4认同)

Fai*_*ali 5

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

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

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

上段没有提到在评估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() );
}
Run Code Online (Sandbox Code Playgroud)

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

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

希望这可以帮助。