我最近遇到了一种情况,我最终得到了大量嵌套的lambdas来构建异步计算链.
template <typename F>
struct node : F
{
node(F&& f) : F{std::move(f)}
{
}
template <typename FThen>
auto then(FThen&& f_then)
{
return ::node{[p = std::move(*this), t = std::move(f_then)]()
{
}};
}
};
int main()
{
auto f = node{[]{ }}.then([]{ }).then([]{ });
return sizeof(f);
}
Run Code Online (Sandbox Code Playgroud)
我在lambda中捕获的所有对象都是空的,但最终对象的大小大于1:gcc.godbolt.org上的示例.
如果我将lambda内部更改为node</* ... */>::then具有显式EBO的函数对象,则最终对象的大小将变为1.
template <typename P, typename T>
struct node_lambda : P, T
{
node_lambda(P&& p, T&& t) : P{std::move(p)}, T{std::move(t)}
{
}
void operator()()
{
}
};
Run Code Online (Sandbox Code Playgroud)
template <typename FThen>
auto node</* ... */>::then(FThen&& f_then)
{
return ::node{node_lambda{std::move(*this), std::move(f_then)}};
}
Run Code Online (Sandbox Code Playgroud)
我发现这真的很烦人,因为我被迫:
写了很多样板代码,大致相当于lambda.
由于像EBO这样的东西不适用于lambda捕获,因此需要支付额外的内存成本.
标准中是否存在明确强制空lambda捕获以获取额外空间的任何内容?如果是这样,为什么?
Bar*_*rry 10
对于由副本捕获的每个实体,在闭包类型中声明未命名的非静态数据成员.
虽然这里的lambdas没有捕获:
auto f = node{[]{ }}.then([]{ }).then([]{ });
Run Code Online (Sandbox Code Playgroud)
因此没有未命名的非静态数据成员,因此是空的,这不是then()实际使用的.它使用这个:
return ::node{[p = std::move(*this), t = std::move(f_then)](){}};
Run Code Online (Sandbox Code Playgroud)
该拉姆达捕获t和p通过拷贝,因此有两个未命名的非静态数据成员.每个都.then()添加另一个成员变量,即使每个变量都是空的,因此节点的大小也会不断增加.
或者换句话说,空基本优化仅适用于基础,而lambdas的捕获不会创建基础,它会创建非静态数据成员.