我有这段代码:
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());
}
它采用局部变量的地址。
第一个例子是否完全安全?
答案 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,这是我对标准书的解释,我觉得它是可以辩护的。
希望这有帮助。