I recently came across a situation where I ended up with a large number of nested lambdas to build asynchronous computation chains.
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);
}
All the objects I capture in my the lambdas are empty, yet the size of the final object is greater than one: example on gcc.godbolt.org.
If I change the lambda inside node</* ... */>::then to a function object with explicit EBO, the size of the final object becomes one.
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()()
{
}
};
template <typename FThen>
auto node</* ... */>::then(FThen&& f_then)
{
return ::node{node_lambda{std::move(*this), std::move(f_then)}};
}
Live example on gcc.godbolt.org
I find this really annoying because I'm forced to either:
Write a lot of boilerplate code that is roughly equivalent to the lambda.
Pay an additional memory cost due to the fact that something like EBO doesn't apply to lambda captures.
Is there anything in the Standard that explicitly forces empty lambda captures to take additional space? If so, why?