为什么try..catch块需要括号?

时间:2010-06-09 18:57:09

标签: c++ syntax try-catch

在if等的其他语句中,如果块中只有一条指令,你可以避免使用大括号,你不能用try ... catch块做到这一点:编译器不会买它。例如:

try
    do_something_risky();
catch (...)
    std::cerr << "Blast!" << std::endl;

使用上面的代码,g ++只是说它在do_something_risky()之前需要一个'{'。为什么try ... catch之间存在这种行为差异,比方说,如果......其他?

谢谢!

9 个答案:

答案 0 :(得分:13)

直接来自C ++规范:

try-block:
    try compound-statement handler-seq

如您所见,所有try-block都期望compound-statement。根据定义,复合语句是用大括号包装的多个语句。

让复合语句中的所有内容确保为try-block生成新范围。在我看来,这也使得一切都变得容易阅读。

您可以在C++ Language Specification

的第359页自行查看

答案 1 :(得分:10)

不确定原因,但一个好处是没有悬空捕捉问题。请参阅dangling-else,了解大括号是可选项时可能出现的歧义。

答案 2 :(得分:7)

try-block的语法是:

try compound-statement handler-sequence

其中handler-sequence是一个或多个处理程序的序列,具有以下语法:

catch (type-specifier-seq declarator) compound-statement
catch (...) compound-statement

这与控制语句等其他语句不同(if,while,for等)。这些的语法是:

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.
}

我知道,按价值捕获是一种不好的做法(例如可能的对象切片等),但我这样做是为了防止讨论异常的存储持续时间并使其易于推理。

现在想想,关于“&#39;”的存储持续时间和联系。你期望的是,&#39; e&#39;只能在调用handleError函数之前引用,但在调用完成后才会引用。它应具有自动存储持续时间,并且在此范围内没有链接#34;。这可能通过隐式定义局部作用域来完成,就像在其他语句中一样,但是使异常声明看起来像一个函数参数可能是一个更好的主意。所以需要块(复合语句)。 Se bellow。

现在想想尝试和之后的声明。没有理由在那里使用关键字,没有理由使用复合语句,但语法可能会变得模糊和复杂。

这就是Stroustrup在Exception Handling for C++中所说的:

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(); } }

但我认为这比try-block的情况要少。 if-statament有两种语法:

if (condition) statement-true
if (condition) statement-true else statement-false

因为有时候不采取其他行动是有道理的。但是没有catch-clause的try-block就没有意义。 &#39;尝试&#39;可以省略但不实用,正如Stroustrup所说,但如果指定了try-block,则catch子句不能。除此之外,可能存在多个与同一个try-block相关的catch,但只有一个基于依赖于catch-clauses的异常类型和顺序的规则执行。

现在,如果将if-else的语法更改为:

,该怎么办?
if (condition) compound-statement-true else compound-statement-false

然后,你必须写if-else像这样:

if (c1) { f(); } else { if (c2) { g(); } else { h(); } }

看到没有&#39; elseif&#39;关键字,没有特殊语法&#39;否则如果&#39;。我认为,即使是“戴上牙套也总是&#39;防守者不喜欢这样写,而是写下来:

if (c1) { f(); } else if (c2) { g(); } else { h(); }

我认为这不是一个强有力的理由来定义上面的语法,并在语言中引入一个&#39; elseif&#39;关键字或为&#39;定义特殊语法,如果&#39;。

答案 3 :(得分:6)

阅读this link。大多数原因似乎是关于管理在真实异常情况下需要创建和销毁的对象的范围和分配。

所以,我的猜测是,C ++的语法编写者要求g ++(或任何符合C ++编译器的标准)的作者为最坏的情况做准备,g ++作者似乎已经这样做了。

答案 4 :(得分:5)

为什么呢?在安全性和向后兼容性之间进行权衡。

从if ...中得到的教训表明,需要括号可以消除错误。现在,ISO C ++人员强烈倾向于向后兼容C,因此他们没有更改if ... else的C语法。但是新的构造需要大括号来划分受控块,因为它们不会出现在旧的C代码中,因此向后兼容性不是问题。

答案 5 :(得分:3)

嗯,首先,这就是语法的工作原理。

其次,我认为目标是强制为异常块生成一个新的范围(如果我错了,请纠正我)。

答案 6 :(得分:1)

这就是they想成为的样子。没有理由,这是一项法律。

答案 7 :(得分:1)

不确定您是否使用.NET,但CLR使用大括号作为标记。

http://dotnet.sys-con.com/node/44398

来自文章:“SEH(结构异常处理)表由一组描述保护代码结构的子句组成。该表有一组描述异常处理子句类型的二进制标志:一个Try Offset标志,它是受保护代码块的开头; Try Length标志,它是受保护代码的长度; Handler Offset和Handler Length标志,详细说明了异常处理程序块的开头它的长度;以及类标记或过滤器偏移标志,取决于定义的异常处理程序的类型。此信息允许CLR确定发生异常时要执行的操作。它映射出受保护的代码块的开头,用于执行异常的代码,以及与过滤或其他特殊情况相关的特殊语义。“

我认为其他框架也会做同样的事情。

答案 8 :(得分:0)

主要是因为

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