【问题标题】:Memory requirements for C++ lambdasC++ lambda 的内存要求
【发布时间】:2015-04-08 12:56:17
【问题描述】:

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

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

【问题讨论】:

  • "C++ Lamdas 产生一个 std::function" 不,他们没有。
  • ok c++ lamdas 产生可以分配给 std::function 的东西

标签: c++ c++11 lambda


【解决方案1】:

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

  • operator()
  • 每次复制捕获一个成员变量
  • 引用捕获的潜在成员变量
  • 如果没有捕获,则转换运算符为指向函数的指针
  • 自动生成复制构造函数
  • 可能是自动生成的移动构造函数

保证有:

  • 默认构造函数
  • 复制/移动赋值运算符

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

【讨论】:

  • 那么如何分配给 std::function?
  • @doron std::function 有一个模板化的构造函数,它接受一个实现 operator () 的对象。
  • @doron 几乎所有带有operator() 的东西都可以分配给std::function。可分配给 std::functionstd::function 的属性,而不是 lambdas 的属性。
【解决方案2】:

Lambda 是一种未命名的类型,可以存储在 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 对象将被复制(或移动,取决于您的语法)到函数对象。在上面的示例中,您的函数对象确实会存储一个向量,但请记住,向量将其有效负载存储在堆上(可能使用自定义分配器)。

【讨论】:

    【解决方案3】:

    C++ lambda 生成 std::function

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

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

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

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

    那么这些捕获的值存储在哪里以及必须满足哪些分配器要求才能使其工作?

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

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

    没有。

    【讨论】:

      【解决方案4】:

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

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

      要么在std::function 本身内部的一个小缓冲区中,要么它会使用默认分配器分配一块内存来存储状态。

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

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

      不包含任何堆分配。

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-12-31
        • 2017-06-22
        • 1970-01-01
        • 2023-04-01
        • 1970-01-01
        • 2011-05-07
        • 1970-01-01
        • 2013-08-05
        相关资源
        最近更新 更多