C++11 lambda 实现和内存模型

标签 c++ memory lambda c++11

我想了解一些关于如何正确思考 C++11 闭包和 std::function 的信息,了解它们是如何实现的以及如何处理内存的。

虽然我不相信过早的优化,但我确实有一个习惯,即在编写新代码时仔细考虑我的选择对性能的影响。我也做了相当多的实时编程,例如在微 Controller 和音频系统上,要避免非确定性的内存分配/释放暂停。

因此,我想更好地了解何时使用或不使用 C++ lambda。

我目前的理解是,没有捕获闭包的 lambda 与 C 回调完全一样。但是,当通过值或引用捕获环境时,会在堆栈上创建一个匿名对象。当必须从函数返回值闭包时,将其包装在 std::function 中。在这种情况下,闭包内存会发生什么?它是从堆栈复制到堆的吗?是否在释放 std::function 时释放它,即它是否像 std::shared_ptr 一样进行引用计数?

我想在一个实时系统中,我可以设置一个 lambda 函数链,将 B 作为延续参数传递给 A,从而创建一个处理管道 A->B。在这种情况下,A 和 B 闭包将被分配一次。虽然我不确定这些是否会分配在堆栈或堆上。然而,一般来说,这在实时系统中使用似乎是安全的。另一方面,如果 B 构造了一些 lambda 函数 C 并返回,那么 C 的内存将被重复分配和释放,这对于实时使用来说是 Not Acceptable 。

在伪代码中,一个 DSP 循环,我认为它将是实时安全的。我想执行处理 block 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; }
}

在后一种情况下,闭包是在循环的每次迭代中构造的,但与前面的示例不同,它很便宜,因为它就像一个函数调用,不进行堆分配。此外,我想知道编译器是否可以“解除”闭包并进行内联优化。

这是正确的吗?谢谢。

最佳答案

My current understanding is that a lambda with no captured closure is exactly like a C callback. However, when the environment is captured either by value or by reference, an anonymous object is created on the stack.

没有;它始终是在堆栈上创建的具有未知类型的 C++ 对象。无捕获的 lambda 可以转换为函数指针(尽管它是否适合 C 调用约定取决于实现),但这并不意味着它 函数指针。

When a value-closure must be returned from a function, one wraps it in std::function. What happens to the closure memory in this case?

lambda 在 C++11 中并没有什么特别之处。它是一个像任何其他对象一样的对象。一个 lambda 表达式会产生一个临时的,可用于初始化堆栈上的变量:

auto lamb = []() {return 5;};

lamb 是一个堆栈对象。它有一个构造函数和析构函数。它将遵循所有 C++ 规则。 lamb 的类型将包含捕获的值/引用;它们将成为该对象的成员,就像任何其他类型的任何其他对象成员一样。

你可以把它交给 std::function:

auto func_lamb = std::function<int()>(lamb);

在这种情况下,它将获得 lamb 值的拷贝。如果 lamb 通过值捕获了任何东西,那么这些值将有两个拷贝; lamb 一个,func_lamb 一个。

当当前作用域结束时,func_lamb会被销毁,接着是lamb,按照清理堆栈变量的规则。

您可以轻松地在堆上分配一个:

auto func_lamb_ptr = new std::function<int()>(lamb);

std::function 内容的内存去向取决于实现,但 std::function 使用的类型删除通常需要至少一个内存分配。这就是 std::function 的构造函数可以采用分配器的原因。

Is it freed whenever the std::function is freed, i.e., is it reference-counted like a std::shared_ptr?

std::function 存储其内容的拷贝。与几乎所有标准库 C++ 类型一样,function 使用 值语义。因此,它是可复制的;当它被复制时,新的 function 对象是完全独立的。它也是可移动的,因此任何内部分配都可以适本地转移,而无需更多的分配和复制。

因此不需要引用计数。

假设“内存分配”等同于“在实时代码中使用不好”,您所说的所有其他内容都是正确的。

关于C++11 lambda 实现和内存模型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12202656/

相关文章:

c++ - 从 void 指针缓冲区进行结构实例化

c++ - 从文件读取导致无限循环的问题

c# - lambda 表达式中带有 Join 的Where 子句

c++ - 类型位长度和特定于体系结构的实现

C++一般如何将成员函数转换为独立函数(用作函数参数)?

lambda - 如何使用 Java 8 Streams 和 Lambda 迭代引用父元素的嵌套 for 循环?

c++ - 如果未使用的函数参数是指向函数的指针,是否需要为其实际参数定义?

c++ - 如何以模块化方式保存和加载 C++ 应用程序状态

c++ - 创建图时内存写入异常

c++ - 如何从函数返回动态分配的数组并正确删除它