簡體   English   中英

"為什么不能通過堆棧指針訪問通過引用捕獲的 C++ 局部變量?"

[英]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);
}
  1. 這是Godbolt 演示<\/a>(擴展了一點)。<\/li>
  2. 我添加了testABI<\/code>以顯示您的函數的參數是由注冊表傳遞的。<\/li><\/ol>
    因此,在test2<\/code>中,首先創建x<\/code>和y<\/code>以在堆棧上創建局部變量,因此可以通過引用傳遞它們。<\/li>
  3. 在堆棧上創建 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM