制作单个捕获Lambda

时间:2018-09-27 20:50:48

标签: c++ lambda capture multiple-instances single-instance

这显然是一个玩具示例,但是可以说我有一个 n 函数,如下所示:

void one(const int param) {
    const auto func = [=](){ return 13 == param; };
}

void two(const int param) {
    const auto func = [=](){ return 13 == param; };
}

以此类推;它们都有一个相同的捕获λ。是否可能有1个lambda实例始终捕获其所在函数的param而不是 n 实例?也许是我应该问的后续问题,编译器是否已识别复制并将复制简化为单个实例?

4 个答案:

答案 0 :(得分:4)

您可以简单地创建一个返回lambda的函数:

auto make_lambda(int param) {
    return [=](){ return 13 == param; };
}

bool one(const int param) {
    return make_lambda(param)();
}

bool two(const int param) {
    return make_lambda(param)();
}

这两个函数将使用相同的生成类(尽管不是相同的实例)。这是生成的代码(使用C++ Insights获得):

__lambda_2_12 make_lambda(int param)
{

  class __lambda_2_12
  {
    public: inline /*constexpr */ bool operator()() const
    {
      return 13 == param;
    }

    private:
    int param;

    public: __lambda_2_12(int _param)
    : param{_param}
    {}

  } __lambda_2_12{param};

  return __lambda_2_12;
}


bool one(const int param)
{
  return make_lambda(param).operator()();
}


bool two(const int param)
{
  return make_lambda(param).operator()();
}

答案 1 :(得分:3)

不幸的是,您将使用此解决方案获得多种类型。 [expr.prim.lambda.closure]/1声明

  

lambda表达式的类型(也是闭包对象的类型)是唯一的未命名非工会类类型,称为闭包类型,其属性如下所述。

强调我的

所以每个

const auto func = [=](){ return 13 == param; };

是它自己的表达式,因此即使它们在语法上是相同的,您也可以得到一个新的唯一类型。

您可以做的是将重复因素分解成函子,然后只有一个已定义的类。

class compare
{
    int val;
public:
    compare(int val) : val(val) {}
    bool operator() const { return val = 13; }
};

然后您的功能将变为

void one(const int param) {
    const auto func = compare{param};
}

void two(const int param) {
    const auto func = compare{param};
}

答案 2 :(得分:3)

lambda的合成闭包类型是唯一的,并且在[expr.prim.lambda.capture]/2中所述的定义点进行了定义:

  

在包含相应的lambda-expression [...]

的最小块范围,类范围或命名空间范围中声明闭包类型

函数的捕获用于创建在函数范围内引入的唯一闭包类型的非静态数据成员:[expr.prim.lambda.capture]/10.2

  

对于每个通过副本捕获的实体,在闭包类型中声明一个未命名的非静态数据成员。这些成员的声明顺序未指定[...]

每个引入的闭包类型都会有所不同,它们的成员取决于定义时捕获的内容。

答案 3 :(得分:1)

您将始终获得不同的类型,但对于每种用途,您可能不会获得不同的 code 。这是链接器的工作。 MSVC链接器以及实验性的gold链接器执行MSVC所谓的“ COMDAT折叠”(我不知道用黄金来称呼),它识别翻译单元内部和之间的相同功能,并将它们合并为一个。