C ++ lambda的内存要求

时间:2015-04-08 12:56:17

标签: c++ c++11 lambda

C ++ lambda产生std::function。为了能够稍后执行lambda,所有这些状态都需要存储在某个地方。那么这些捕获的值存储在哪里以及必须存在哪些分配器要求才能工作?

如果需要,还有什么方法可以改变这种行为吗?

4 个答案:

答案 0 :(得分:4)

lambda表达式不会创建std::function对象。相反,它创建了一个名为闭包类型的未命名类类型的对象(闭包对象)。该闭包类型将具有以下组件:

  • operator()
  • 每个按副本捕获的成员变量
  • 通过引用捕获的潜在成员变量
  • 如果没有捕获,则转换操作符为指向函数的指针
  • 自动生成复制构造函数
  • 可能是自动生成的移动构造函数

保证有:

  • 默认构造函数
  • 复制/移动分配运算符

标准没有其他保证。闭包类型的成员变量与任何其他成员变量一样,具有自动存储持续时间;也就是说,它们包含在闭包对象本身中。没有特殊的分配器要求,也无法改变这些成员的管理方式。

答案 1 :(得分:4)

Lambdas是一种未命名的类型,可以存储在std::function中。那里的重要区别。您可以将lambda的声明视为与仿函数类的声明非常相似。例如:

std::vector<int> vec(......);    
auto& lambda = [vec](int x) -> void {};

我们创建了一个vec的副本,并在堆栈的调用时接收一个int。在这种情况下,你的lambda大致相当于这个类

class mylambda
{
public:
   mylambda(const std::vector& vecin) : vec(vecin) {}
   void operator() (int x) const {}

private:
   std::vector<int> vec;
};

请注意,将复制您的按值捕获。参考捕获基本上作为指针存储。很可能你的编译器会尽可能长时间地推迟lambda的构造。但最终,捕获将存储在对象中。

返回std::function - 如果您选择将lambda复制到函数对象中,因为您可能希望将其传递到拥有函数之外,那么您的lambda对象将被复制(或移动,具体取决于您的语法)进入函数对象。在上面的示例中,您的函数对象确实会存储一个向量,但请记住,向量会将其有效负载存储在堆上(可能使用自定义分配器)。

答案 2 :(得分:2)

  

C ++ lambda产生std::function

不,他们没有。它们生成一个类类型的对象,重载operator(),以便可以像函数一样调用它们。

std::function是任何可调用类型的包装器,包括lambdas。

  

为了能够稍后执行lambda,所有这些状态都需要存储在某个地方。

实际上,捕获的值需要存储在lambda中。

  

那么这些捕获的值存储在哪里以及必须提供哪些分配器要求?

捕获的值存储为lambda类的成员。对局部变量的引用可能会被类似地处理;或者可能被优化以捕获指向包含它们的堆栈帧的单个指针。

  

如果需要,还有什么方法可以改变这种行为吗?

没有

答案 3 :(得分:1)

除了lambdas和std::function之间的混淆之外,至少部分问题可以改为:

  

std::function在哪里存储其状态?“。

std::function本身的小缓冲区中,或者它将使用默认分配器分配一块内存来存储状态。

尽管类型T的通用对象是否适合第一类或第二类,但std::reference_wrapper 保证使用小缓冲区优化,但很大程度上未指定;也就是说,以下一行:

std::function<void()> f = std::ref( function_object );

不包含任何堆分配。

  

如果需要,还有什么方法可以改变这种行为吗?

查看std::function构造函数的the definition:其中大多数都使用额外的Allocator参数,允许您指定自定义分配器。我假设分配器(如果有状态)将被复制并在分配器本身提供的内存中进行类型擦除。