【发布时间】:2012-08-25 12:32:33
【问题描述】:
我想要一些关于如何正确思考 C++11 闭包和 std::function 的信息,了解它们的实现方式和内存处理方式。
虽然我不相信过早的优化,但我确实有一个习惯,即在编写新代码时仔细考虑我的选择对性能的影响。我也做了相当多的实时编程,例如在微控制器和音频系统上,要避免非确定性的内存分配/释放暂停。
因此,我想更好地了解何时使用或不使用 C++ lambda。
我目前的理解是,没有捕获闭包的 lambda 与 C 回调完全一样。但是,当通过值或引用捕获环境时,会在堆栈上创建一个匿名对象。当必须从函数返回值闭包时,将其包装在std::function 中。在这种情况下,闭包内存会发生什么?它是从堆栈复制到堆的吗?是否在释放 std::function 时释放它,即它是否像 std::shared_ptr 一样进行引用计数?
我想在一个实时系统中,我可以设置一个 lambda 函数链,将 B 作为延续参数传递给 A,从而创建一个处理管道 A->B。在这种情况下,A 和 B 闭包将被分配一次。虽然我不确定这些是否会分配在堆栈或堆上。然而,一般来说,这在实时系统中使用似乎是安全的。另一方面,如果 B 构造了一些 lambda 函数 C 并返回,那么 C 的内存将被重复分配和释放,这对于实时使用来说是不可接受的。
在伪代码中,一个 DSP 循环,我认为它将是实时安全的。我想执行处理块 A,然后是 B,A 调用它的参数。这两个函数都返回std::function 对象,所以f 将是一个std::function 对象,其环境存储在堆中:
auto f = A(B); // A returns a function which calls B
// Memory for the function returned by A is on the heap?
// Note that A and B may maintain a state
// via mutable value-closure!
for (t=0; t<1000; t++) {
y = f(t)
}
我认为在实时代码中使用它可能不好:
for (t=0; t<1000; t++) {
y = A(B)(t);
}
还有一个我认为堆栈内存可能用于闭包的地方:
freq = 220;
A = 2;
for (t=0; t<1000; t++) {
y = [=](int t){ return sin(t*freq)*A; }
}
在后一种情况下,闭包是在循环的每次迭代中构造的,但与前面的示例不同,它很便宜,因为它就像一个函数调用,不进行堆分配。此外,我想知道编译器是否可以“解除”闭包并进行内联优化。
这是正确的吗?谢谢。
【问题讨论】:
-
使用 lambda 表达式时没有开销。另一种选择是自己编写这样一个函数对象,这将完全相同。顺便说一句,在内联问题上,由于编译器拥有它需要的所有信息,它肯定可以内联对
operator()的调用。没有“提升”可做,lambdas 没有什么特别的。它们只是局部函数对象的简写。 -
这似乎是一个关于
std::function是否将其状态存储在堆上的问题,与lambdas无关。对吗? -
只是为了防止误解:lambda表达式不是
std::function!! -
附注:从函数返回 lambda 时要小心,因为通过引用捕获的任何局部变量在离开创建 lambda 的函数后都会变得无效。
-
@Steve 从 C++14 开始,您可以从具有
auto返回类型的函数返回 lambda。