I noticed that compilers implement capture by reference by making an array on the stack of pointers to the captured locals, which can be passed to the lambda to access them. That surprised me because the compiler knows where the locals are relative to the stack pointer, so I thought it could just pass the stack pointer. That would make one less indirection in the lambda and save the work of putting the pointers on the stack. I was wondering why the compiler can't do that?
For example, this C++:
#include <functional>
extern void test(std::function<void()>& f);
int test2(int x, int y)
{
std::function<void()> f([&]() { x += y; });
test(f);
return x;
}
generates this assembly on Clang 13 -O3 (comments mine):
mov dword ptr [rsp + 8], edi // put x on the stack
mov dword ptr [rsp + 12], esi // put y on the stack
lea rax, [rsp + 8]
mov qword ptr [rsp + 16], rax // put &x on the stack
lea rax, [rsp + 12]
mov qword ptr [rsp + 24], rax // put &y on the stack
mov qword ptr [rsp + 40], offset std::_Function_handler<void (), test2(int, int)::$_0>::_M_invoke(std::_Any_data const&)
mov qword ptr [rsp + 32], offset std::_Function_handler<void (), test2(int, int)::$_0>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation)
lea rdi, [rsp + 16]
call test(std::function<void ()>&)
and similar on GCC and MSVC.