43

I came across this syntax recently for try-catch for function.

struct A
{
  int a;

  A (int i) : a(i)  // normal syntax
  {
    try {}
    catch(...) {}
  }

  A ()   // something different
  try : a(0) {}
  catch(...) {}

  void foo ()  // normal function
  try {}
  catch(...) {}
};

Both syntax are valid. Is there any technical difference between these syntax apart from coding style ? Is one of the syntax superior to other by any aspect ?

Alok Save
  • 202,538
  • 53
  • 430
  • 533
iammilind
  • 68,093
  • 33
  • 169
  • 336

4 Answers4

45

The First Syntax:
The scope of the try block starts after the Member Initialization list has been completed, So any exception thrown during Member Initialization will not be caught by this try-catch block.

The second syntax:
It ensures that if an exception gets thrown during Member Initialization list then you are able to catch the exception.

The Third Syntax:
It ensures that any exception thrown from betwen the starting brace of the try block inside the function body gets caught appropriately, It would mean any exception caused during the argument passing(if any can occur) will not be caught in this try-catch block.

So yes they are disinctly different in what functionality they provide.


EDIT:
Some guidelines to be considered while using the second syntax(function-try-block) in constructors & destructors:

As per the C++ Standard,

If the catch block does not throw (either rethrow the original exception, or throw something new), and control reaches the end of the catch block of a constructor or destructor, then the original exception is automatically rethrown.

In Simple words:
A constructor or destructor function-try-block's handler code MUST finish by emitting some exception.

Guideline 1:
Constructor function-try-block handlers have only one purpose -- to translate an exception. (And maybe to do logging or some other side effects.) They are not useful for any other purpose.

Throwing a exception from destructors is an bad idea, Take a look here to know why.
Guideline 2:
Destructor function-try-blocks have no practical use at all. There should never be anything for them to detect, and even if there were something to detect because of evil code, the handler is not very useful for doing anything about it because it can not suppress the exception.

Guideline 3:
Always clean up unmanaged resource acquisition in local try-block handlers within the constructor or destructor body, never in constructor or destructor function-try-block handlers.


For Standardese Fans:

C++ standard, clause 15.3, paragraph 15:

If a return statement appears in a handler of the function-try-block of a constructor, the program is ill-formed.

C++ standard, clause 15.3, paragraph 16:

The exception being handled is rethrown if control reaches the end of a handler of the function-try-block of a constructor or destructor. Otherwise, a function returns when control reaches the end of a handler for the function-try-block (6.6.3). Flowing off the end of a function-try-block is equivalent to a return with no value; this results in undefined behavior in a value-returning function (6.6.3).


References:
Have a look at this must read resource here for more details & explanation.

Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • @iammilind: the difference is that exception is re-thrown from `function-try-block`, that makes it of little use. – Gene Bushuyev Jul 20 '11 at 04:58
  • 1
    For the third one, did you mean exceptions caused during the argument passing *will* be caught (you currently have "will not be caught"). – Matt Smith Jul 20 '11 at 04:59
  • 1
    The explanation is incomplete to the point of being incorrect. – Gene Bushuyev Jul 20 '11 at 05:50
  • 1
    @Matt Smith: The try block starts with the opening brace of try, So I meant exactly what I said. – Alok Save Jul 20 '11 at 05:54
  • @Als: I provided a link to a pretty complete and detailed explanation. There wouldn't be any benefit repeating it here. – Gene Bushuyev Jul 20 '11 at 06:05
  • 1
    @Als: I believe that there is a specific behavior for function try/catch blocks in case you do not return in the `catch`, and that is the exception is rethrown. I may apply only to destructors... not sure. – Matthieu M. Jul 20 '11 at 06:18
  • @Matthieu: it always re-throws the same exception, unless one throws a different exception or uses `return` in `catch` block. So in essence, `function-try-block` isn't used to catch exceptions, rather to translate them. – Gene Bushuyev Jul 20 '11 at 06:29
  • @Gene: thanks for clearing that up :) I must admit it's not a feature I have ever used... – Matthieu M. Jul 20 '11 at 07:00
  • 1
    @Gene Bushuyev: §15.3/16 says that the exception will be rethrown in the case of a *function-try-block* in a constructor or destructor, but that if the *function-try-block* is in a regular function then the it is equivalent to a return with no value. – David Rodríguez - dribeas Jul 20 '11 at 07:58
  • @Als: If you have some time you should try to improve the response by reading [this](http://www.gotw.ca/gotw/066.htm). I agree that this is missing enough information to be misleading to others, including the final remark *should be done appropriately suited situations*. With the information present in the answer it might look like a good idea to have a *function-try-block* in a constructor to catch initialization errors... but the use case is very limited and not represented at all here. – David Rodríguez - dribeas Jul 20 '11 at 08:02
  • Accepting; because it's more understandable answer. – iammilind Jul 20 '11 at 09:02
  • @David Rodríguez - dribeas: Thanks for the comment, I modified the answer, ofcourse the article is more in iteself an excellent resource. – Alok Save Jul 20 '11 at 09:12
  • @Matthieu M.: Modifed the answer. – Alok Save Jul 20 '11 at 09:13
  • 2
    @Matthieu M. §15.3/15 *If a **return** statement appears in a handler of the function-try-block of a **constructor**, the program is **ill-formed**.* §15.3/16 *The exception being handled is **rethrown if control reaches the end of a handler of the function-try-block of a constructor or destructor**. Otherwise, a function returns when control reaches the end of a handler for the function-try-block (6.6.3). Flowing off the end of a function-try-block is equivalent to a return with no value; this results in undefined behavior in a value-returning function (6.6.3).* – David Rodríguez - dribeas Jul 20 '11 at 18:43
9

Function-try-block is useful mostly in constructors, because there is no other way of catching exceptions in initialization list. In destructors one must be careful to return in catch block, because exception will be automatically re-thrown. (And in good design destructors must not throw.) In normal functions this feature is not useful. Edit: an old but still good article: http://drdobbs.com/184401316

Gene Bushuyev
  • 5,512
  • 20
  • 19
  • Interesting article, thank you. Bear in mind though that SO policy is to fight hyperlink rot by summing up the article. Just so that your answer does not become meaningless if the link went dead. – Matthieu M. Jul 20 '11 at 06:20
  • @Matthieu: good rule, with two problems: summary is often much worse than original, and ... I'm just too lazy for that :-) – Gene Bushuyev Jul 20 '11 at 06:31
  • then your answer is doomed to stay at the bottom where nobody will read it :/ Too bad since the link is quite interesting. – Matthieu M. Jul 20 '11 at 07:00
  • The exception is *rethrown* only in the case of *function-try-block* s in constructors or destructors. In the rest of the functions the behavior is exactly that of a *try-block* that enclosed all the other code in the function. – David Rodríguez - dribeas Jul 20 '11 at 07:56
  • @David Rodríguez: thanks for correcting *re-throwing* part. But it still not exactly the same as normal *try-block*, you can't access the local variables in function body from catch, because they are all in try-block. So I don't see where *function-try-block* could be possibly useful in ordinary functions, normal *try-block* is always better solution. – Gene Bushuyev Jul 20 '11 at 17:45
  • @Gene: If you wrap the _entire_ body in the `try` block, the local variables will not be visible from the `catch` block, either. So I think @David is right and there is no semantic difference for ordinary functions. (That said, I agree with you that there is no reason to use a _function-try-block_ except for constructors.) – Nemo Jul 20 '11 at 18:09
  • @ Nemo : true, but with *function-try-block* you don't have a choice, and just by adding too extra braces you get more flexibility. – Gene Bushuyev Jul 20 '11 at 18:16
3

Might as well cite the spec... Or at least a draft.

Section 15 (4):

A function-try-block associates a handler-seq with the ctor-initializer, if present, and the compound-statement. An exception thrown during the execution of the compound statement or, for constructors and destructors, during the initialization or destruction, respectively, of the class’s subobjects, transfers control to a handler in a function-try-block in the same way as an exception thrown during the execution of a try-block transfers control to other handlers.

(Here the handler-seq is the stuff after the catch and the compound-statement is the function body.)

So the "function try block" on a constructor or destructor catches exceptions thrown by the ctor-initializers and by the construction or destruction of subobjects.

On a function other than a constructor or destructor, it is the same as simply wrapping the function body. (Well, as far as I can discern from reading the spec.)

Interesting feature, and new to me. Thanks for bringing it up.

Nemo
  • 70,042
  • 10
  • 116
  • 153
  • *catches exceptions thrown by the ctor-initializers and by the construction or destruction of subobjects* -- it's quite an understatement. – Gene Bushuyev Jul 20 '11 at 05:52
  • *On a function other than a constructor or destructor, it is the same as simply wrapping the function body* -- it's most definitely not! – Gene Bushuyev Jul 20 '11 at 05:53
  • @Gene: Explain? Your own answer says "In normal functions this feature is not useful". (Note that the "automatic re-throw" behavior you so cleverly allude to only happens for constrictors and destructors.) So, explain? – Nemo Jul 20 '11 at 13:25
  • @ Nemo: I included comment about normal functions in my answer above. – Gene Bushuyev Jul 20 '11 at 17:47
2

The "something different" example puts the processing of the initializer list within the scope of the try block.

Adam Mitz
  • 6,025
  • 1
  • 29
  • 28
  • If that were the only difference. The significant difference is that it's impossible to access the object members in constructor *function-try-block* and at the end of *catch* the original exception is re-thrown (unless taken care). – Gene Bushuyev Jul 20 '11 at 05:48