以下的C++代码是否有效?
int main() noexcept
{
}
使用 clang++ 3.8.0 和 g++ 7.2.0 编译这份代码没有问题(使用编译参数 -std=c++14 -O0 -Wall -Wextra -Werror -pedantic-errors
)。
main
函数的 noexcept
声明中是否允许使用复杂条件(例如包含 noexcept
操作符)?
C++17 呢?据我所知,在此标准修订版中,noexcept
声明成为函数类型的一部分。
以下的C++代码是否有效?
int main() noexcept
{
}
使用 clang++ 3.8.0 和 g++ 7.2.0 编译这份代码没有问题(使用编译参数 -std=c++14 -O0 -Wall -Wextra -Werror -pedantic-errors
)。
main
函数的 noexcept
声明中是否允许使用复杂条件(例如包含 noexcept
操作符)?
C++17 呢?据我所知,在此标准修订版中,noexcept
声明成为函数类型的一部分。
标准文档 [[basic.start.main]] 对 main
函数规定了以下约束条件:
实现应该允许:
— 返回 int 的无参数函数和
— 返回 int 的带有 (int, char 指针的指针) 参数的函数
此外:
定义 main 为已删除或声明 main 为内联、静态或 constexpr 的程序是非法的。
实践中,没有关于 main
的 noexcept
限定符的规范。而另一方面,noexcept
可以作为任何函数的限定符。这意味着 main noexcept
不是非法的。
noexcept
的 main 有什么区别?由于标准对于 main
函数的 noexcept
限定符不是很明确,我们可以尝试推断一些行为并检查实现。
从这里可得:
每当抛出异常并且搜索处理程序时遇到非抛出函数的最外层块时,就会调用
std::terminate
函数。
而对于异常的一般规则,可以从这里得知:
如果抛出异常并且未捕获,包括逃逸线程的初始函数、main 函数以及任何静态或线程本地对象的构造函数或析构函数,那么将调用
std::terminate
。关于未捕获异常是否进行堆栈展开是由实现定义的。
这意味着在 main
函数中,无论 main
是否带有 noexcept
限定符,都始终会生成 std::terminate
调用。
确实,以下代码:
int main(int argc, char* argvp[]) {
throw 1;
return 0;
}
和
int main(int argc, char* argvp[]) noexcept {
throw 1;
return 0;
}
将会产生相同的汇编输出。例如在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
这意味着无论有没有声明noexcept
,在“main level”时,由于堆栈帧为空,它都将被解析为对std::terminate
的调用。
int main() noexcept
和简单的 int main()
有什么区别? - Constructorint main() noexcept;
中的main
类型是“返回int
的()
函数”,在C++17中是“返回int
的noexcept ()
函数”。前者明确要求由[basic.start.main]支持,而后者则没有。这似乎是C++17中的缺陷。
int main()
和int main() noexcept
会产生不同的结果,但我无法快速做到。可能你是对的,根本就没有这样的例子。 - Constructornoexcept
函数抛出异常,则永远不会发生堆栈展开。而如果您尝试通过main
抛出异常,则可能会发生堆栈展开;这取决于具体的实现。 - Nicol Bolas