【问题标题】:Why does this lambda in C++ include every reference?为什么 C++ 中的这个 lambda 包含每个引用?
【发布时间】:2019-06-07 11:17:20
【问题描述】:

使用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;
}

输出为1,8,16,24,表示L2包含1个引用,L3包含2个,L4包含3个。

但是,给定相同的函数“[&amp;](){L1, L2;} in main()”,&amp;L1 - &amp;L2 的值应该是固定的,并且要使用带有指向 L2 的指针的 L1,x86 中有直接寻址 @987654331 @假设rbx=&amp;L2。为什么 GCC 仍然选择在 lambda 中包含每个引用?

【问题讨论】:

  • 你编译时禁用了优化(默认为-O0),所以想知道为什么 G++ 没有做出更明智的选择有点傻。尽管即使启用了优化,我们仍然得到相同的大小。 godbolt.org/z/a46Nq4
  • @PeterCordes 已编辑以显示优化级别无关紧要,适用于 64 位
  • 您可以将此报告为一个错过优化的 GCC 错误 (gcc.gnu.org/bugzilla),看看 gcc 是否可以捕获一系列本地变量,而不是单独存储指向每个本地变量的指针。跨度>
  • 也许 ABI 问题也与此相关。
  • @MikeLui:为什么?我很确定 ISO C++ 不会对这样的实现细节有任何话要说。这不是关于语言规则的问题。如果 C++ 标准的相关部分限制了实现选择,则 C++ 标记覆盖。

标签: c++ optimization lambda x86 g++


【解决方案1】:

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

捕获 lambda 本身并不是一个函数,并且不能衰减/转换为函数指针,因此我认为 lambda 对象不需要任何布局。 (Use a lambda as a parameter for a C++ function)。读取 lambda 对象的生成代码将始终从定义它的同一编译单元生成。所以听起来似乎合理的是,它只需要一个用于所有本地人的基指针,并带有偏移量。

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


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

【讨论】:

  • 诚然,“错过”的优化与当前编译器技术永远不可能发生的某些转换之间存在一条模糊的界限,但我认为这属于后一类。有人可能还会问,为什么编译器不重新组织对象,以防更紧凑的表示是可能的,或者执行其他深度数据混杂转换。编译器基本上不会。您拥有的主要内容是标量化,并且很多看起来很聪明的东西都不会发生,但我不认为会发生这里建议的事情。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-04-11
  • 2017-01-11
  • 2022-01-20
  • 1970-01-01
  • 2013-04-25
  • 2010-11-08
  • 1970-01-01
相关资源
最近更新 更多