No curly braces around main() — why does this work

2019-02-04 07:47发布

问题:

I'm working through a book on C++ and in the chapter on errors it has this program (I left a couple of minor things out but it's mostly this):

int main()
try { 
        // our program (<- this comment is literally from the book)
        return 0;
}
catch(exception& e) {
    cerr << "error: " << e.what() << '\n';
    return 1;
}
catch(...) {
    cerr << "Unknown exception\n";
    return 2;
}

This compiled but of course it did nothing so I'm still wondering about

  1. why there isn't a set of curly braces enclosing everything after main()? Are the blocks or shall I call them "catchphrases" (ha!) part of main() or not?
  2. If they are functions how come there's no "int" before catch(whatever)?
  3. If they're not functions, what are they?
  4. re catch(...), I've never seen ellipses used that way. Can I use ellipses anywhere to mean "anything"?

回答1:

1 why there isn't a set of curly braces enclosing everything after main()? ...

There is, it just has the keyword try before the opening brace, and some catch blocks after the end of main.

... Are the blocks or shall I call them "catchphrases" (ha!) part of main() or not?

2 If they are functions how come there's no "int" before catch(whatever)?

3 If they're not functions, what are they?

They're keywords, not functions, and they're part of main, despite the try coming between the int main() definition and its {} body. See the motivating case below for another example.

4 re catch(...), I've never seen ellipses used that way. Can I use ellipses anywhere to mean "anything"?

there are a couple of overloaded meanings of ellipsis in C++.

  • catch(...) means catch anything, it's like a wildcard for the exception type (and should be the last catch if you have several)
  • int printf(char *, ...) means a function takes a variable argument list, which entirely disables type-checking of arguments and is easy to get wrong (but occasionally useful)
  • template <typename... TypePack> means a template accepts a variable type list, which is very useful for metaprogramming but completely out of scope here
  • #define DEBUG(str, ...) is a variadic macro, analogous to the variable-argument function

Function-level try/catch blocks are a way of wrapping the whole function body in an exception handler. So, here, the main function block is inside the try { ... }.

IIRC this was introduced specifically to allow constructors to wrap their initializer lists with a try/catch, to handle exceptions thrown from subobject constructors.

Eg. (motivating case)

C::C() try : Base(1), member(2)
{ ...
}
catch (...) {
    // handle Base::Base or member construction failure here
}

Note that there is no other way to catch exceptions thrown from base class or member subobject constructors, because they'll always do at least default construction before your constructor body starts, even if you omit the initializer list.



回答2:

If you use a try block directly instead of braces, the try block catches everything that happens in the function. This is useful in constructors so you can catch exceptions in the initialization list.

This will catch exceptions from bar's constructor:

void Foo::Foo() try : bar() {
    // do something
} catch(...) {
    // do something
}

This will only catch exceptions in the body:

void Foo::Foo() : bar() {
    try {
        // do something
    } catch(...) {
        // do something
    }
}


回答3:

That is a rarely used feature of the language. You can enclose a whole function in a try catch block:

void f()
try 
{         // function starts here
...
}         // function ends here
catch (...) 
{}

The feature is rarely used as it is almost never useful. In the case of a regular function, you can just have a try/catch block that covers all the code of the function, so in this case the feature would not be needed. It has limited functionality in constructors, as it allows catching exceptions on the initializer list, that cannot be enclosed in a try/catch block otherwise.

But the problem there is that there is very little that can be done in the catch block: construction failed so an exception must be thrown (the catch block can throw a different exception to what was caught, but it has to throw). At the same time, you cannot really perform any cleanup, since at the point where the exception is caught you might not know which of the elements in the initialization list threw, which means that you might not know (in the general case) which of the member objects have been constructed or not.



回答4:

It's function-try block. Yes, it's legal, but not needed in more cases. Look at this answer for explanations. What is the meaning of this syntax?



回答5:

Not sure about the missing curly braces around the main function, but it looks like others answered that all ready.

As for the rest of your question, try catch blocks are not functions outside the main, and do not need an int in front of them.

try {}: The program will try to run anything within this block, if it fails for whatever reason, it will be caught by the catch blocks.

catch (exception) {}: When an exception is thrown, it will be caught by a catch block with the specific Exception as its parameter, and allow for the programmer to handle that case.

catch (...) {}: Should catch anything that makes it through the first catch block..



回答6:

1) That's a feature of C++, function try-catch block (see here, for example)
2) And yes, catch(...) { /* absolutely any exception gets caught here */ }