制作一个单一的捕获 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 怎么称呼它),它识别翻译单元内部和之间的相同功能并将它们合并到一。
这显然是一个玩具示例,但假设我有一个 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 怎么称呼它),它识别翻译单元内部和之间的相同功能并将它们合并到一。