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