异常是否应该中断break/continue/return语句?

9
我在处理语言时遇到了一个有趣的流程控制场景。当处理 break 语句时发生异常会发生什么呢?GCC 认为 break 流程丢失,但标准似乎没有明确规定应该发生什么。
例如,以下程序应该做什么?
#include <iostream>
using namespace std;

struct maybe_fail {
    bool fail;
    ~maybe_fail() {
        if( fail )
            throw 1;
    }
};

int main() {
    for( int i=0; i < 6; ++i ) {
        cout << "Loop: " << i << endl;

        try {
            maybe_fail mf;
            mf.fail = i % 2;
            if( i == 3 )
                break;

        } catch( int ) {
            cout << "Caught" << endl;
        }
    }
    return 0;
}

请注意,return语句也将被拦截,continue同理(添加catch后的输出以查看)。试图跳出块外部的goto也会被捕获。
正确的流程是什么?标准似乎没有涉及到这一点:第6.6节关于跳转语句并没有提到,异常处理的第15节也没有。我确实明白,在析构函数中使用异常会导致非常糟糕的结果,但如果您正在使用像BOOST_SCOPE_EXIT这样的延迟语句,这种行为可能变得非常重要。
有趣的是,Java和Python中也发生了相同的情况,因此至少在命令式语言中似乎存在一些一致性。

你的问题似乎证实了C++ FAQ中的建议:http://www.parashift.com/c++-faq-lite/dtors-shouldnt-throw.html 基本上,不要在析构函数中抛出异常。FAQ中的这个问题说的是如果你处于“双重异常处理”情况下,将会调用terminate(),但是你的例子似乎继续了这个建议,即这样做是不好的。 - Kevin Anderson
1
@Kevin,完全正确,这个建议也适用于任何语言中的任何“defer”语句(包括C++中的BOOST_SCOPE_EXIT)。 - edA-qa mort-ora-y
你还能如何解释这种行为呢?在析构函数中抛出异常,然后呢? - bdwain
@bdwain,throw语句的结尾可以继续使用break语句并退出循环(当有人编写break时,意图是退出循环,因此这样做同样合理,甚至更合理)。 - edA-qa mort-ora-y
@bdwain,break语句不会在当前作用域结束,它需要跳出整个循环。因此,异常正好处于break流程的中间,而不是之前或之后。 - edA-qa mort-ora-y
显示剩余4条评论
2个回答

4
这个内容涉及到15.1抛出异常的部分:
2 当抛出异常时,控制权会转移到最近的处理程序,其类型与异常相匹配(15.3); “最近”表示由控制线程最近进入并且尚未退出try关键字后面的复合语句或ctor-initializer的处理程序。
一旦控制权转移到异常处理程序,它就从那里继续执行。 没有机制可以“记住”代码处于break中间,然后在处理异常后以某种方式恢复。

我不确定这足够清晰。请考虑6.6节要求跳转语句按照指示执行,并未提及对该流程的中断。也就是说,我认为您所述的要求和跳转要求存在冲突。 - edA-qa mort-ora-y
@edA-qamort-ora-y 如果引用标准中“在跳转发生之前调用析构函数”这样的表述,是否能回答这个问题? - Drew Dormann
@edA-qamort-ora-y:说实话,我看了6.6(特别是第二段),我不确定我是否看到了歧义。 - NPE
问题在于跳转语句没有提到“尝试”转移流程,只是说它们会这样做。在非常类似的15.1控制转移中,也有类似的控制转移。请考虑goto使用短语“无条件转移”,这比异常更强烈,但它也被异常捕获。 - edA-qa mort-ora-y
正确。异常在“break”之后发生,显然是因为它发生在离开作用域时,这在“break”之前不可能发生。 - MSalters

0
鉴于标准中的具体措辞,我认为它是含糊不清的。这段文字不足以确定这些情况的预期流程。现在我们所遵循的惯例很可能只是编译器编写方式的偶然结果。
请参考我的博客主题如何捕获“return”语句

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接