为什么try..catch块需要大括号?
而在if … else等其他语句中,如果块中只有一条指令,则可以避免使用大括号,但不能使用try … catch块来实现:编译器不会购买它。 例如:
try do_something_risky(); catch (...) std::cerr << "Blast!" << std::endl;
有了上面的代码,g ++只是说在do_something_risky()之前需要一个“{”。 为什么这种尝试之间的行为差异…赶上,如果…其他?
谢谢!
直接从C ++规范:
try-block: try compound-statement handler-seq
正如你所看到的,所有的try-block
一个compound-statement
。 按照定义,复合语句是用大括号括起来的多个语句。
让复合语句中的所有内容都能确保为try-block生成一个新的作用域。 在我看来,这也使得一切都变得容易一些。
您可以在C ++语言规范的第359页上自行检查它
不知道为什么,但一个好处是没有悬挂的问题。 请参阅dangling-else在括号可选时可能出现的歧义。
阅读这个链接 。 大部分原因似乎是pipe理在实际例外情况下需要创build和销毁的对象的范围和分配。
所以,我的猜测是,C ++的语法作者正在要求g ++(或任何符合C ++编译器的标准)的作者为最糟糕的情况做好准备,而g ++作者似乎也这样做了。
那么,首先,这就是语法的工作原理。
其次,我相信目标是为exception块强制产生一个新的范围(纠正我,如果我错了)。
为什么? 安全性和向后兼容性之间的权衡。
从…中学到的经验表明,需要大括号可以消除错误。 现在,ISO C ++的人对C的向后兼容性有强烈的偏好,所以他们没有改变if … else的C语法。 但是新的构造需要花括号来划分受控块,因为它们不会出现在旧的C代码中,因此向后兼容性不是问题。
他们就是这样想的 没有理由,这是一个法律。
try-block的语法是:
try compound-statement handler-sequence
其中handler-sequence是一个或多个处理程序的序列,它们具有以下语法:
catch (type-specifier-seq declarator) compound-statement catch (...) compound-statement
这与控制语句(如,等等)等其他语句不同。 这些语法是:
if (condition) statement-true else statement-false while (condition) statement for (init-statement; condition; iteration_expression) statement etc.
现在,问题是为什么在try-block中需要复合语句而不是单个语句?
想想这个代码:
int main() { // before try-statement. try g(); catch (std::runtime_error e) handleError(e); // after try-statement. }
我知道,抓价值是一个不好的做法(例如,可能的对象切片等),但我这样做是为了防止关于exception的存储持续时间的讨论,并使其容易推理。
现在想想,关于'e'的存储时间和链接。 你所期望的是,'e'只能在调用handleError函数之前被引用,而在调用完成之后才能被引用。 在这个“范围”里应该有自动存储时间,没有联动。 这可能可以通过像在其他语句中那样隐式地定义一个局部范围来完成,但是使exception声明看起来像一个函数参数可能是一个更好的主意。 所以块(复合语句)是需要的。 Se波纹pipe。
现在想想那个之后的尝试和声明。 没有理由使用关键字try在那里,没有理由使用复合语句,但是语法可能变得模棱两可和复杂。
这就是Stroustrup在C ++exception处理中所说的:
It might be possible to simplify the try { ... } catch (abc) { ... } syntax by removing the apparently redundant try keyword, removing the redundant parentheses, and by allowing a handler to be attached to any statement and not just to a block. For example, one might allow: void f() { g(); catch (x1) { /* ... */ } } as an alternative to - 28 - void f() { try { g(); } catch (x1) { /* ... */ } } The added notational convenience seems insignificant and may not even be convenient. People seem to prefer syntactic constructs that start with a prefix that alerts them to what is going on, and it may be easier to generate good code when the try keyword is required.
并经过更详细的解释:
Allowing exception handlers to be attached to blocks only and not to simple statements simplifies syntax analysis (both for humans and computers) where several exceptions are caught and where nested exception handlers are considered (see Appendix E). For example, assuming that we allowed handlers to be attached to any statement we could write: try try f(); catch (x) { ... } catch (y) { ... } catch (z) { ... } The could be interpreted be in at least three ways: try { try f(); catch (x) { ... } } catch (y) { ... } catch (z) { ... } try { try f(); catch (x) { ... } catch (y) { ... } } catch (z) { ... } try { try f(); catch (x) { ... } catch (y) { ... } catch (z) { ... } } There seems to be no reason to allow these ambiguities even if there is a trivial and systematic way for a parser to chose one interpretation over another. Consequently, a { is required after a try and a matching } before the first of the associated sequence of catch clauses.
正如Stroustrup所说,没有大括号,这个陈述可能意味着不同的事情取决于规则,你可能需要用括号来澄清内涵。 我们可以用Stroustrup例子中的if语句使一些看起来复杂吗? 当然,我们可以,例如:
if (c1) if (c2) f(); else if (c3) g(); else h();
这实际上相当于:
if (c1) { if (c2) f(); else { if (c3) g(); else h(); } }
但是我认为这比尝试块的问题更less。 if-statament有两种语法:
if (condition) statement-true if (condition) statement-true else statement-false
因为有时候不要有别的行动是有道理的。 但是,没有条款限制的尝试块是没有意义的。 Stroustrup说,'try'可以省略但是不实用,但是如果你指定了一个try-block,catch-clause就不行。 除此之外,可能有多个捕获涉及相同的try-block,但只有一个是基于依赖于catch-clause的exceptiontypes和顺序的规则执行的。
现在,如果if-else的语法改为:
if (condition) compound-statement-true else compound-statement-false
那么,你必须写如果 – 否则像这样:
if (c1) { f(); } else { if (c2) { g(); } else { h(); } }
看到没有'elseif'关键字,没有'else if'的特殊语法。 我认为,即使是“戴上牙套总是”的防御者也不喜欢这样写,而是写下这个:
if (c1) { f(); } else if (c2) { g(); } else { h(); }
我认为这不是定义上述语法的强有力的理由,并在语言中引入“elseif”关键字或为“else if”定义特殊语法。
不知道你是否使用.NET,但CLR使用大括号作为标志。
http://dotnet.sys-con.com/node/44398
从文章中:“SEH(结构exception处理)表由一组描述被保护代码结构的子句组成,表中有一组描述exception处理子句types的二进制标志: 一个Try Offset标志,它是守护代码块的开始;一个Try Length标志,它是守护代码的长度; Handler Offset和Handler Length标志,它们详细说明exception处理程序块的开始及其长度;以及一个类别令牌或filter偏移标志,取决于定义的exception处理程序的types,这个信息允许CLR确定发生exception时该怎么做,它映射出被守护的代码块的开始,执行exception的代码和特殊与过滤或其他特殊情况有关的语义。“
我会假设其他框架做同样的事情。
主要是因为
if (a) int b = 10; else int b = 5; b += 5;
会失败,因为if … else without {}是一个语法快捷方式
if (a) { int b = 10; } else { int b = 5; } b += 5;
它明确地告诉你int b
与软件的其他部分不在一个范围内。
如果我没有弄错,以下也失败了
a ? int b = 10 : int b = 5; b += 5;
当然,你的编译器可能会为你优化代码……但是由于if / else语句中的作用域,它在技术上应该会失败。
无论何时您看到您正在定义软件的范围。
-Stephen