为什么C ++中的lambda包含所有引用?

l4m*_*4m2 5 c++ optimization x86 lambda g++

使用g++.exe -m64 -std=c++17和任何优化级别进行编译,然后运行:

#include <iostream>

int main() {
    const auto L1 = [&](){};
    std::cout << sizeof(L1) << std::endl;
    const auto L2 = [&](){L1;};
    std::cout << sizeof(L2) << std::endl;
    const auto L3 = [&](){L1, L2;};
    std::cout << sizeof(L3) << std::endl;
    const auto L4 = [&](){L1, L2, L3;};
    std::cout << sizeof(L4) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

输出为1,8,16,24,这意味着L2包含1个引用,L3包含2个引用,L4包含3个引用。

但是,给定相同的功能“ [&](){L1, L2;}in main()”,的值&L1 - &L2应固定,并L1L2x 的指针一起使用,在x86中直接[rbx+const]假定为rbx=&L2。为什么GCC仍然选择在lambda中包含所有引用?

Pet*_*des 1

我认为这是一个错过的优化,因此您可以在https://gcc.gnu.org/bugzilla/上将其报告为 gcc bug 。使用missed-optimization关键字。

捕获 lambda 本身并不是一个函数,并且不能衰减/转换为函数指针,因此我认为 lambda 对象不需要任何布局。(使用 lambda 作为 C++ 函数的参数)。读取 lambda 对象的生成代码将始终从定义它的同一编译单元生成。因此,它只需要所有局部变量的一个基指针以及与之相距的偏移量,这听起来似乎是合理的。

如果它们彼此之间的偏移量不是编译时或至少不是链接时常量,则对具有非自动存储类别的变量的其他捕获可能仍然需要单独的指针。(或者这可能是一个单独的优化。)


实际上,您可以通过将 lambda 传递给模板函数,让编译器使用该空间并在内存中创建 lambda 对象__attribute__((noinline))https://godbolt.org/z/Pt0SCC