Why do try..catch blocks require braces?

前端 未结 3 1595
深忆病人
深忆病人 2021-02-11 13:32

While in other statements like if ... else you can avoid braces if there is only one instruction in a block, you cannot do that with try ... catch blocks: the compiler doesn\'t

相关标签:
3条回答
  • 2021-02-11 13:46

    The syntax of try-block is:

    try compound-statement handler-sequence
    

    where handler-sequence is a sequence of one or more handlers, which have the following syntax:

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

    This is different from other statements like control statements (if, while, for, etc). The syntax for these are:

    if (condition) statement-true else statement-false  
    while (condition) statement
    for (init-statement; condition; iteration_expression) statement
    etc.
    

    Now, The question is why compound-statement is needed in the try-block instead of a single statement?

    Think about this code:

    int main()
    {
      // before try-statement.
    
      try g(); catch (std::runtime_error e) handleError(e);
    
      // after try-statement.
    }
    

    I know, catch by value is a bad practice (e.g. possible object slicing, etc), but I did it in order to prevent a discussion about the storage duration of the exception and make it easy to reason about.

    Now think, about the storage duration and linkage of 'e'. What you expect, is that 'e' only can be referred just before the call to handleError function, but no after the call is completed. It should have automatic storage duration and no linkage in this "scope". This could probably done by implicitly define a local scope like in other statements, but make the exception-declaration looks like a function parameter was probably a better idea. So the block (compound-statement) is needed. Se bellow.

    Now think about the try and the statement after that. There is no reason to use the keyword try there, and no reason to use a compound statement, but the syntax could become ambiguous and complicated.

    This is what Stroustrup said about it in 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.  
    

    And after a more detailed explanation:

    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.
    

    As Stroustrup said, without the braces, the statement could mean different things depending on the rule and you will probably need to put braces to clarify the intension. Can we make some that looks complicated with the if-statement like in Stroustrup's example? Of course we can, something like this for example:

    if (c1) if (c2) f(); else if (c3) g(); else h();
    

    This is actually equivalent to:

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

    But I think this is less problematic than the case of try-block. There is two syntax for the if-statament:

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

    because it make sense not to have a else action sometimes. But it make no sense a try-block without a catch-clause. The 'try' can be omitted but not practical, as Stroustrup said, but the catch-clause can not if you specified a try-block. Beside of this, there could be more than one catch related to the same try-block but only one is executed based in rules that depends on the exception type and order of the catch-clauses.

    Now, what if the syntax of if-else is changed to:

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

    then, you must write if-else like this:

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

    See that there is no 'elseif' keyword, no special syntax for 'else if'. I think that even the 'put braces always' defenders don't like to write like this, and write this instead:

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

    I think that this is not a strong reason to define the syntax as above and introduce in the language a 'elseif' keyword or define a special syntax for 'else if'.

    0 讨论(0)
  • 2021-02-11 13:52

    Straight from the C++ spec:

    try-block:
        try compound-statement handler-seq
    

    As you can see, all try-blocks expect a compound-statement. By definition a compound statement is multiple statements wrapped in braces.

    Have everything in a compound-statement ensures that a new scope is generated for the try-block. It also makes everything slightly easier to read in my opinion.

    You can check it yourself on page 359 of the C++ Language Specification

    0 讨论(0)
  • 2021-02-11 13:56

    Not sure why, but one benefit is that there is no dangling-catch issue. See dangling-else for an ambiguity that can arise when braces are optional.

    0 讨论(0)
提交回复
热议问题