对不起,这是一个冗长的问题,但让我分解一下:
C ++标准是否保证:
void (*Ptr)(void) = [] {};
return Ptr;
仍将定义行为?
我理解,对于一个闭包,它将被定义,因为闭包对象是按值移动/复制的;但是,虽然我知道'常规'函数具有无限/无生命,但Ptr的目标是否具有相同的?或者它是否被lambda的每个实例化破坏并重新创建?
我关心的原因是,如果不是,我不能使用lambdas作为回调。我想知道。
答案 0 :(得分:5)
物体有生命; 功能没有。功能不存在或死亡;他们总是存在。因此,函数不能“超出范围”,先前有效的函数指针所指向的函数也不会消失。无论它们来自何处,函数指针始终有效。
现在,这忽略了动态加载等等,但这是超标准的行为。
从lambda返回的函数指针是一个函数指针。这不是特别的或神奇的。因此,它与任何其他函数指针的行为没有区别。
转换为void(*)()的结果是否可能指向调用绑定到某个对象的成员函数的东西?
这是一个更复杂的问题。标准似乎相当不明确的一个。标准只说:
函数的地址,在调用时,与调用闭包类型的函数调用操作符具有相同的效果。
“同样的效果”究竟意味着什么。有人可能会争辩说“相同的效果”意味着执行函数调用操作符所执行的操作,执行相同的语句序列。人们还可以争辩说“相同的效果”意味着调用闭包对象本身。
后一种情况可能听起来很难实现,但请记住,可以使用编译器魔法。闭包可以返回一个特定于实例的函数指针,由请求时由闭包分配。或者其他一些。
在这个问题上,非规范性文本似乎不是很明确。有一个(通用)lambda的闭包的例子,它说:
template<class T> auto operator()(T t) const { ... } template<class T> static auto lambda_call_operator_invoker(T a) { // forwards execution to operator()(a) and therefore has // the same return type deduced ... }
有问题的评论建议转发,但这需要静态调用构造一个新的lambda实例来进行转发。
总的来说,标准没有明确说明在关闭破坏后调用生成的函数指针是否合法。
答案 1 :(得分:2)
lambda函数只是真实函数或函子的语法糖(即具有operator()
的对象和一些成员,通常在构造时间中定义)。因此,在编译期间静态定义了这样的函数或方法。
尽管标准可能没有完全指定一种确切的实现方式,正如@NicolBolas指出的那样,实际实现似乎遵循严格的准则:没有上下文的lambda可以转换为普通函数指针,不创建中间对象既不是在lambda定义的地方,也不是在调用的地方。我刚刚检查了它(再次)gcc和clang,我几乎可以肯定MSVC也是如此。
注意:其余部分是关于带有上下文的lambdas,虽然对我来说这似乎更有趣和实际,但问题明确涉及无上下文的lambdas。
上下文存储在lambda中(想象一个带有一些有意义参数的functor对象,在对象构造时收到)。因此,如果您将一些引用或指针传递给上下文,这些引用和指针(例如this
)将不会自动延长其相应实体的生命周期。这就是为什么当你将lambda保存在一个非定义的范围内时,你应该格外小心。
在此resolved issue中查看与lambda及其上下文范围定义相关的问题示例。检查修复程序,看看做了什么来使存储的lambdas与上下文安全。