制作一个单一的捕获 Lambda

Make a Single Capturing Lambda

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

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

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

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

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

The type of a lambda-expression (which is also the type of the closure object) is a unique, unnamed non-union class type, called the closure type, whose properties are described below.

强调我的

所以每个

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

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

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

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};
}

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

The closure type is declared in the smallest block scope, class scope, or namespace scope that contains the corresponding lambda-expression [...]

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

For each entity captured by copy, an unnamed non-static data member is declared in the closure type. The declaration order of these members is unspecified [...]

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

你可以简单地创建一个函数 returns 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)();
}

这两个函数将使用相同的生成 class(但不是它的同一个实例)。这是生成的代码(使用 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()();
}

您将始终获得不同的类型,但您可能不会每次使用都获得不同的代码。这是链接器的工作。 MSVC 链接器,以及实验性的 gold 链接器,执行 MSVC 调用的 "COMDAT folding"(我不知道 gold 怎么称呼它),它识别翻译单元内部和之间的相同功能并将它们合并到一。