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.