I have a suggestion for an additional rule regarding function try blocks for constructors/destructors. As [except.handling]/14 [1] states that if control flow reaches the end of a handler of a constructor鈥檚/destructor鈥檚 function try block, the handled exception is rethrown. To me (and I think for others it might be too) this case is surprising and therefore I suggest to always state this behavior explicitly (at least in the case mentioned), i.e. insert a throw; or return statement at the end of all handlers of constructor鈥檚/destructor鈥檚 function try block. I'm curious whether others consider this as an issue too.
[1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/n4741.pdf
Possible rule:
throw at end of constructor鈥檚/destructor鈥檚 function try blockIf controlflow reaches the end of a constructor's/destructor鈥檚 handler of a function try block the handled exception will be implicitly rethrown. The behavior is different in any other catch handler. Therefore, the desired behavior should be stated explicitly in this case.
struct Resource {
Resource() noexcept(false) try
{
allocationThatMightThrow();
}
catch (const AllocationException & e)
{
doSomeCleanup(e);
}
};
struct Resource {
Resource() noexcept(false) try
{
allocationThatMightThrow();
}
catch (const AllocationException& e)
{
doSomeCleanup(e);
throw;
}
};
Flag catch handlers of a constructor鈥檚 function try block without explicit throw statement. Flag catch handlers of a destructor鈥檚 function try block without explicit return or throw statement.
The example isn't a good use of a function try block. I think "don't use function try blocks" is a better guideline that applies more often.
I disagree with the proposal as written now because except.handle 13 says
If a return statement (9.6.3) appears in a handler of the function-try-block of a constructor, the program is ill-formed.
This contradicts your rule Explicitly return or throw at end of constructor鈥檚/destructor鈥檚 function try block.
Oh dear, how could I miss [except.handle]/13. This makes the return part completely wrong indeed.
Suggesting to avoid function try blocks (at least for constructors/destructors) seems to be reasonable for me too.
Suggesting to avoid function try blocks (at least for constructors/destructors)
Avoid them everywhere.
Avoid them everywhere.
I was going to say "what about converting and rethrowing exceptions", but then I never had to do that myself.. So I just ran Debian Code Search for \)\s*try\s*: (not perfect since it won't catch ones with line breaks) and it found 243 uses in 30 packages, Almost all of them were compiler/IDE/other C++ parser test suites or that regex latching on to something that isn't C++.
I only found three uses in the entire Debian:
valid = false; in the catch clause.+1 for "Avoid them everywhere".
Although I recognise most functions shouldn't have function-try blocks, the suggestion to avoid it, is it because:
or
If (2), I suppose this would apply to putting try-catch on a for-, while- and if-block as well. I do use try-blocks as for-loop bodies sometimes, if they handle events that are allowed to fail independently, without preventing all subsequent events from being executed.
I have a few example of cases where function try-blocks are convenient. For instance (because they save an indentation level):
main() function of a utility where any error propagated to that level should print an error and return a non-zero status code. example@cubbimew I suppose my code didn't show up in your code search.
Windows uses function try on ABI boundaries with the main reason being concision. I like @arvidn points and think the main() example is illustrative (and I have written that myself many times). if guidelines say never I need to learn more about what I'm getting wrong.
We will clarify the existing rules about function-try block for constructors/destructor.
@arvidn
If (2), I suppose this would apply to putting try-catch on a for-, while- and if-block as well.
No, not at all. A try-catch block is a statement, so I've never met anybody who is surprised that you can use it as the body of a for or while loop. It's no different to a single statement without braces:
while (cond)
++i; /* a single statement
while (cond)
{ /* a block statement */; }
while (cond)
try { } catch (...) { };
But a function try block is unique syntax, allowing try to appear outside a function body. You can't do that with anything else:
void func() while (cond) { }
void func(int i) ++i; { }
It's a completely false analogy to say that if people aren't familiar with function try blocks they aren't familiar with other perfectly normal uses of try-catch.
It's a completely false analogy to say that if people aren't familiar with function try blocks they aren't familiar with other perfectly normal uses of them.
It is my personal experience that it's common to learn about these uses of try-catch blocks together, and colleagues being surprised of both. But obviously I can't claim this is the case universally.
My impression is that function try blocks are little known because many, probably most, textbooks don't mention them.
Most helpful comment
Although I recognise most functions shouldn't have function-try blocks, the suggestion to avoid it, is it because:
or
If (2), I suppose this would apply to putting try-catch on a
for-,while- andif-block as well. I do use try-blocks asfor-loop bodies sometimes, if they handle events that are allowed to fail independently, without preventing all subsequent events from being executed.I have a few example of cases where function try-blocks are convenient. For instance (because they save an indentation level):
main()function of a utility where any error propagated to that level should print an error and return a non-zero status code. example@cubbimew I suppose my code didn't show up in your code search.