我认为已接受的答案如果不是错误的话,就相当缺乏信息,所以即使过了这么多年,我仍然觉得有必要提供一个适当的答案。推测编译器实现者选择不花费精力在任何特定功能上的原因只是...推测。异常只在异常情况下抛出的事实通常不会被视为不优化此类代码的原因。相反,尽管抛出代码没有优化非抛出代码,但异常抛出和处理基础架构仍然被非常小心地优化。此外,那段代码可能感觉很牵强,不值得考虑,但这并不是真的:它可能来自更复杂的代码的内联和优化,将其优化可能导致更简单的代码,从而允许其他优化传递触发,或者包含函数进一步内联。像这样的优化传递,当正确且有效地实现时,总是值得考虑的,无论原始代码看起来多么牵强。否则,即使是像死代码消除这样的基本传递也会被避免,因为“首先不应编写死代码”。这显然不是这种情况。
因此,我不同意被接受的答案。异常应该仅在特殊情况下抛出,这并
不是代码未被优化的原因。
原因纯粹是技术性的,并在clang开发邮件列表中解释了:
http://lists.llvm.org/pipermail/cfe-dev/2015-March/042035.html
简而言之,语言允许在
catch
块内调用的代码在任何时候“没有看到”异常对象的情况下重新抛出异常:
void g() { throw; }
因此,请考虑OP代码:
int f()
{
try { throw std::runtime_error("Boo!"); }
catch ( const std::exception &e ) { std::cout << e.what() << std::endl; }
}
对于编译器而言,
e.what()
或两个
operator<<
的调用可能会重新抛出异常,因此优化掉异常处理代码将破坏程序的语义。确保不会发生这种情况需要“整个程序知识”,如上面的电子邮件中所述。甚至更简单的情况也可以进行优化,例如:
int func() {
try {
throw 42;
}catch(int x) {
return x;
}
}
上述代码可以转换为return 42
。没有技术上的障碍。
然而,大多数常见编译器不会这样做(godbolt)。从上面链接的电子邮件中,我们可以了解到,Clang开发人员(对于其他编译器,我们无法说些什么)认为这种优化并不值得,可能是因为它只适用于不进行函数调用的catch
块。
无论如何,该消息并未说明他们是否会接受补丁来执行此操作。