28

Is the following code valid in C++?

int main() noexcept
{
}

Both clang++ 3.8.0 and g++ 7.2.0 compile it fine (with -std=c++14 -O0 -Wall -Wextra -Werror -pedantic-errors compilation flags).

Is it allowed to use complex conditions (e.g. including noexcept operator) in the noexcept specification of the main function?

And what about C++17? As I know noexcept specifier becomes the part of the function type in this revision of the standard.

Constructor
  • 7,273
  • 2
  • 24
  • 66
  • I cannot find anything about the main function concerning noexcept in the Standard, implying it's allowed, but then again I'm drunk, so hopefully someone will join in. – DeiDei Nov 30 '17 at 22:50
  • 8
    What exactly would it matter? If an exception escapes `main`, `std::terminate` must be called. If an exception exits a `noexcept` function, `std::terminate` must be called. For all useful purposes, `main` *already is* `noexcept`. And since you can neither call `main` nor get a pointer to it... what does it matter? – Nicol Bolas Nov 30 '17 at 23:20
  • @NicolBolas Interesting note. I thought I could find an example of the code where throwing `int main()` and `int main() noexcept` give different results but I can't do it quickly. Possibly you are right and there is no such example at all. – Constructor Nov 30 '17 at 23:31
  • @Constructor: There is one difference. If you try to throw though a `noexcept` function, then stack unwinding *never* occurs. Whereas if you attempt to throw through `main`, stack unwinding *might* occur; it's implementation-dependent. – Nicol Bolas Nov 30 '17 at 23:35
  • @NicolBolas It is strange but *clang++* shows an exactly opposite behavior: it looks like [with `noexcept` it does stack unwinding](http://coliru.stacked-crooked.com/a/1d0fbcc8435872ae) and [without `noexcept` it doesn't](http://coliru.stacked-crooked.com/a/27867e535a42c178). – Constructor Nov 30 '17 at 23:43
  • @Constructor That sounds like a bug more than anything else. – Passer By Dec 01 '17 at 03:51
  • @NicolBolas No, in both cases whether unwinding occurs is implementation-defined. – T.C. Dec 01 '17 at 18:38
  • @T.C.: Where does the standard say that? Unwinding does not happen until after a handler has been found. If `noexcept` is encountered before a handler has been found, then the standard says `std::terminate` gets called. I don't see where it allows the implementation to invoke unwinding to the `noexcept` function. – Nicol Bolas Dec 01 '17 at 19:20
  • 2
    @NicolBolas [except.terminate]/2. – T.C. Dec 01 '17 at 19:22

2 Answers2

22

The standard [[basic.start.main]] specifies the following constraints on the main function:

An implementation shall allow both:

— a function of () returning int and

— a function of (int, pointer to pointer to char) returning int

Moreover:

A program that defines main as deleted or that declares main to be inline, static, or constexpr is ill-formed.

In practice, there is no specification about noexcept qualifier for main. On the other hands, noexcept is allowed as a specifier for whatever function. That would imply main noexcept is not ill-formed.


What's difference w/o noexcept main?

Since the standard is not very explicit about noexcept for main function as we've seen, we can try to deduct some behaviour and check the implementations.

From here:

Whenever an exception is thrown and the search for a handler encounters the outermost block of a non-throwing function, the function std::terminate is called.

While the general rule for exceptions, from here:

If an exception is thrown and not caught, including exceptions that escape the initial function of std::thread, the main function, and the constructor or destructor of any static or thread-local objects, then std::terminate is called. It is implementation-defined whether any stack unwinding takes place for uncaught exceptions.

This implies that throw from main function always generates a std::terminate invocation. Regardless of noexcept specification of the main.

Implementation side:

Indeed, the following codes:

int main(int argc, char* argvp[]) {
  throw 1;
  return 0;
}

and

int main(int argc, char* argvp[]) noexcept {
  throw 1;
  return 0;
}

will produce the same output assembly. For example in GCC:

main:
        movl    $4, %edi
        subq    $8, %rsp
        call    __cxa_allocate_exception
        xorl    %edx, %edx
        movl    $1, (%rax)
        movl    typeinfo for int, %esi
        movq    %rax, %rdi
        call    __cxa_throw

That means it will be resolved into an invocation of std::terminate because the stack frame is empty at "main level" regardless of noexcept specification.

Community
  • 1
  • 1
BiagioF
  • 9,368
  • 2
  • 26
  • 50
8

The type of main in int main() noexcept; is "function of () returning int" in C++14 and "noexcept function of () returning int" in C++17.

The former is explicitly required to be supported by [basic.start.main]. The latter isn't.

This looks like a defect in C++17.

T.C.
  • 133,968
  • 17
  • 288
  • 421
  • 4
    Someone who is more enthusiastic about reporting core issues than I am should contact EDG's Mike Miller, the CWG chair, so that - assuming that it hasn't been reported previously - the issue will pop up on the core issues list two years from now. – T.C. Dec 01 '17 at 18:49
  • _the issue will pop up on the core issues list two years from now_ So optimistic… :D – Language Lawyer Jan 06 '20 at 17:39