延迟动态初始化抛出的异常为何无法被main的function-try-block捕获?
对于具有静态存储期的非块非inline变量,其动态初始化是在
main的第一条语句之前执行,还是被延迟执行,这是实现定义的。
若被延迟执行,则它会在对与待初始化变量同翻译单元中的任何非inline函数或非inline变量进行非初始化odr-use之前强序发生。此类延迟动态初始化发生在哪些线程、程序的哪些点,是实现定义的。
- [basic.start.dynamic] p5
实际中,这意味着:
int do_throw() { throw 0; } int x = do_throw(); // 异常是在main()之前抛出还是在main()内部抛出,由实现定义。 int main() try { // 此处可能抛出异常,因为我们并未odr-use main() // (main无法被odr-use,见[basic.start.main]) return x; } catch (...) {}
注:仅定义main并不属于对该函数的odr-use,见[basic.def.odr] p8。这使得将do_throw()的执行序列安排在main内部是合法的。
若初始化被延迟,且main内部对x的odr-use触发了main内部的动态初始化,那么我们有理由认为main外围的function-try-block会捕获该异常。然而:
在[...]或与具有静态存储期的非块变量关联的对象的构造函数中抛出的异常,不会被
main函数上的function-try-block捕获。
- [except.handle] p11
与直觉相反,即使x的动态初始化被延迟,main上的function-try-block也不会捕获该异常。
问题
该行为是疏漏还是有意设计?是所有try块都会忽略延迟动态初始化的异常,还是仅main的function-try-block如此?
该如何实现这种行为?main的function-try-block必须选择性忽略源自延迟动态初始化的某些异常,却捕获其他来源的异常。
进一步思考
这种隐含的行为极为令人惊讶:
int main() try { throw 0; // 安全,异常会被捕获 } catch (...) {} int main() try { return x; // 因未捕获的异常,std::terminate会被调用?!?! } catch (...) {} int main() { try { return x; // 安全?! } catch(...) {} }
注:try块是main内部的第一条语句,因此若发生延迟初始化,return x的执行序列不能早于try块。我不确定这是否可实现,因为main仅有一条语句(除非我们递归考虑所有语句)。
内容的提问来源于stack exchange,提问作者Jan Schultke

