49

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 buy it. For instance:

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

With the code above, g++ simply says it expects a '{' before do_something_risky(). Why this difference of behavior between try ... catch and, say, if ... else ?

Thanks!

user2864740
  • 60,010
  • 15
  • 145
  • 220
Bidou
  • 491
  • 4
  • 4
  • 13
    Because that is how the spec is written. – Justin Niessner Jun 09 '10 at 18:58
  • 1
    And how often do you have a single statement that needs to have a try around it? – Oded Jun 09 '10 at 19:00
  • 2
    Because the designers decide so. Do we always do things, like naming variables, naming file name, etc consistently? – Kevin Le - Khnle Jun 09 '10 at 19:01
  • 3
    At one time try/catch were macros that did a setjmp/longjmp thing. Maybe that's why? – Paul Tomblin Jun 09 '10 at 19:02
  • Today I had to use boost::spirit::qi::phrase_parse, and I had only that statement in the try block. Indeed, this is an extreme case, but I was curious to know the reason why I got that error from g++. – Bidou Jun 09 '10 at 19:03
  • 2
    @Paul Not in Standard C++ they weren't. –  Jun 09 '10 at 19:03
  • 20
    +1 A good question. As I use braces to enclose all blocks, I'd never realised this was the case. Answers that say "that's what the grammar says" are not really responsive. –  Jun 09 '10 at 19:08
  • @Neil, I'm talking about really early days - I started using C++ when it was "cfront" which "compiled" the C++ into C that you then fed to the C compiler. – Paul Tomblin Jun 09 '10 at 19:20
  • 1
    @Paul I'm also an ex cfront user - at no time did it implement try/throw/catch as macros. –  Jun 09 '10 at 19:28

9 Answers9

13

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

Justin Niessner
  • 242,243
  • 40
  • 408
  • 536
  • 21
    With respect, this answer is unhelpful. That the standard requires it is self-evident. The question was not "Do try-catch blocks require braces", but "Why do...". – Catskul Feb 27 '12 at 16:36
  • 12
    *“Have everything in a compound-statement ensures that a new scope is generated for the try-block.”* Scopes in C++ do not depend on curly braces. Also in `if (n > 0) std::vector v(n);`, a new scope (containing only `v`) is opened after the closing parenthesis and ends right at the semicolon. – 5gon12eder Sep 09 '14 at 16:58
10

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.

R Samuel Klatchko
  • 74,869
  • 16
  • 134
  • 187
10

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

Alejandro
  • 101
  • 1
  • 4
  • 2
    None of this is very convincing. The example in the last quote is no different from the situation with `if-else`: `else` goes to the nearest `if`, if you wanna change that - use `{}`. I don't see why this rule could not have been adopted with `try-catch`. – AnT stands with Russia Oct 13 '16 at 04:23
  • The stroustroup quotes are spot on, the other text not so much. – Remember Monica Mar 27 '18 at 04:39
6

Read this link. Most of the reason appears to be about managing the scope and allocation of objects that need to be created and destroyed in case of real exceptions.

So, my guess is, the grammar writers of C++ are asking the authors of g++(or any standards complying C++ compiler) to prepare it for the worst possible cases, and g++ authors appear to have done so.

vpit3833
  • 7,817
  • 2
  • 25
  • 25
6

Why? A tradeoff between safety and backwards compatibility.

The lessons learnt from if...else showed that requiring braces eliminates errors. Now, the ISO C++ people have a strong preference for backwards compatibility with C, so they didn't change the C syntax for if...else. But new constructs require braces to demarcate controlled blocks, as they won't appear in old C code and therefore backwards compatibility is not a concern.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • 3
    If that's the reason then I thank backward compatibility for my "if () if () else ;" constructs. I like those things, and am very much competent to use them appropriately. It's certainly possible for a human to be so. (And of course it is also in the spirit of C++ to give one enough rope to hang oneself with, regardless of how competent any actual programmers are; so I would be a bit surprised to see hard confirmation that this really was the rationale in the case of try-catch.) – mjwach Mar 15 '16 at 17:53
3

Well, first, that's how the grammar works.

Second, I would believe that the goal is to forcibly generate a new scope for the exception blocks(correct me if I'm wrong).

Paul Nathan
  • 39,638
  • 28
  • 112
  • 212
1

That's how they wanted to be. There is no justification, it's a law.

INS
  • 10,594
  • 7
  • 58
  • 89
1

Not sure if you're using .NET but the CLR uses the braces as flags.

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

From the article: "The SEH (structure exception handling) table consists of a set of clauses that describe the structure of the guarded code. The table has a set of binary flags that describe the type of exception handling clause: a Try Offset flag, which is the beginning of the guarded code block; a Try Length flag, which is the length of the guarded code; Handler Offset and Handler Length flags, which detail the beginning of the exception handler block and its length; and a Class Token or Filter Offset flag, depending on the type of Exception Handler that was defined. This information allows the CLR to determine what to do when an exception occurs. It maps out the beginning of the guarded code block, the code to execute for an exception, and special semantics related to filtering or other special circumstance."

I would assume that other frameworks do the same thing.

Nate Noonen
  • 1,371
  • 9
  • 19
-1

Mainly it's because

if (a)
    int b = 10;
else 
    int b = 5;
b += 5;

Will fail because the if...else without {} is a syntax shortcut for this

if (a) {
    int b = 10;
} else {
    int b = 5;
}
b += 5;

which explicitly tells you that int b is in a different scope than the rest of the software.

If I'm not mistaken the following also fails

a ? int b = 10 : int b = 5;
b += 5;

Granted, your compiler might optimize that code for you... but it should technically fail because of the scopes in the if/else statement.

Whenever you see {} you're defining the scope of the software.

-Stephen

Stephen Furlani
  • 6,794
  • 4
  • 31
  • 60
  • 2
    good point about the scope, still this does not answer the question (or probably I did not get your point). – nico Jun 09 '10 at 20:16
  • 2
    Each if, else if, and else block defines its own scope regardless of whether you use braces, so I wouldn't think that you'd need they try or catch blocks to have braces to ensure that they have their own scopes. So, I don't really see how the scoping issue would answer the question - though you do obviously want the try and catch blocks to have their own scopes. – Jonathan M Davis Jun 09 '10 at 23:28
  • My comment was that all clauses force scope definition. If/Else statements without {} are a legacy component from when code was written line-by-line - they are a syntaxical short-cut for putting those clauses in. You should always code like the second example. The reason that things like try/catch require {} is that they have no legacy equivalent that needs support. – Stephen Furlani Jun 10 '10 at 11:25
  • LOL. Not to be rude, but there are plenty of people who would disagree with you bigtime that you should always use braces in an if/else statement or in a loop. Sure, some people (obviously you included) think that it's good style, but just like folks will argue over the best place to put braces, they'll argue over whether you should always use braces in an if/else statement. I, for one, would argue against it if it's not needed. There are good reasons for both styles, but it's primarily a matter of preference and what works for you. – Jonathan M Davis Jun 10 '10 at 18:20