[英]Why can't C++ locals captured by reference be accessed through the stack pointer?
我注意到编译器通过在指向捕获的局部变量的指针堆栈上创建一个数组来实现引用捕获,可以将其传递给 lambda 以访问它们。 这让我感到惊讶,因为编译器知道本地变量相对于堆栈指针的位置,所以我认为它可以只传递堆栈指针。 这将减少 lambda 中的间接性并节省将指针放入堆栈的工作。 我想知道为什么编译器不能这样做?
例如,这个 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;
}
在 Clang 13 -O3 上生成此程序集(我的评论):
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 ()>&)
在 GCC 和 MSVC 上类似。
您看到的不是未优化的 lambda,而是
std::function<\/code>周围的所有内容。
如果您将代码简化为:
template < typename F>
void test(F& f )
{
f();
}
int test2(int x, int y)
{
auto f=[&]() { x += y; };
test(f);
return x;
}
int main()
{
return test2(1,2);
}
我添加了testABI<\/code>以显示您的函数的参数是由注册表传递的。
<\/li><\/ol>
因此,在test2<\/code>中,首先创建x<\/code>和y<\/code>以在堆栈上创建局部变量,因此可以通过引用传递它们。
<\/li>
在堆栈上创建 lambda 之后,将x<\/code>和y<\/code>的地址保存到堆栈中。
使用 lambda 时要调用的函数的地址(没有 vtable 的多态性)也被添加到堆栈上的 labda<\/li><\/ol> 现在你不知道test<\/code>会对f<\/code>做什么。
它可以克隆f<\/code>所以在这种情况下f<\/code>必须从堆栈复制到其他地方。
另一方面,编译test<\/code>时编译器不知道通过了哪种 lambda。
创建副本可能有副作用,也可能没有。 因此,必须可以创建副本并且必须与预定义的 ABI 兼容。
因此,只有当编译器可以知道
test<\/code>的内容时,您所描述的这种快捷方式才有可能。
当不知道
test<\/code>内容时,必须完全创建
f<\/code>才能在需要时克隆它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.