以下非模板化(或者是吗?)函数返回非泛型、有状态 lambda,
auto foo(double a) {
return [a](double b) -> double {
return a + b;
};
}
用 GCC 或 Clang 编译成这个样子。为什么?
foo(double):
ret
我希望它会生成根本没有输出。
我什至不记得为什么我开始写上面的片段,但无论如何......
最初我认为它应该编译成稍微长一点的东西,因为我期望至少看到一个
add
指令。
但是后来我意识到上面的
foo
不能在cpp中单独编译,然后链接到使用它的另一个TU,因为我什至无法为它编写声明!
所以我想说的是,在非头文件中编写该函数的唯一原因是它在该非头文件中使用,此时编译器可能可以在任何使用它的地方内联它。
但如果是这样的话……那么为什么要将
foo
复制到 anything,如果它是 TU 中唯一的东西呢?没有任何东西可以链接,那么为什么要为其生成 any 输出?
使用
struct
+operator()
不会改变任何东西,因为这
struct Bar {
double a;
double operator()(double b) const {
return a + b;
}
};
Bar bar(double a) {
return Bar{a};
}
生成相同的仅限
ret
的代码,事后看来这也是显而易见的,因为如果 bar
隐藏在 cpp 文件中,则甚至无法从其他 TU 链接到此 Bar
函数。
看起来像是 GCC 和 Clang 可能不会尝试寻找的错过的优化。 他们必须运行额外的优化过程,寻找像这样返回 lambda 的函数,并将它们视为
inline
,这样他们就可以避免在不需要的 TU 中发出独立的定义。
但如果他们这样做,他们将无法在链接时检测到违反单一定义规则的情况。
因此,这是不将其视为
inline
并且不发出 asm 的充分理由。
但我猜他们可能只发出一个
.global foo
; foo:
定义 ODR 违规检测的符号,无需在 ret
上花费任何代码大小。 这将是生成 asm 的特殊情况,并且可能不值得在 GCC 或 Clang 内部添加额外的代码来跟踪它应该得到的特殊处理并执行它。
这种优化的价值非常小,因为正如您所说,这只是实际程序中的死代码,其中该函数位于 TU 中,没有任何调用它的东西。 寻找它会花费编译时间,并且是 GCC / Clang 开发人员必须维护的代码。