为什么立即函数默认情况下不是noexcept的,而它们允许设置为noexcept(false)?

3
从 C++20 开始,我们可以使用 consteval 说明符来定义即时函数。当一个函数声明为 consteval 时,每次调用该函数都必须产生编译时常量,否则程序就是非法的。此外,自从 C++20 开始,常量求值上下文中允许使用 try-catch 块,但仍然禁止抛出异常。由于这个原因,我最初认为 consteval 暗示 inline,它也暗示 noexcept,因为抛出任何异常都是被禁止的。正如你现在所想象的那样,这是不正确的:除非你指定了 noexcept,否则一个即时函数就是一个可能抛出异常的函数,具有这种特性所带来的所有负面影响。我是否有不知道的原因呢?

1
比如哪些负面影响? - Nicol Bolas
@JesperJuhl: 你不能“法律挑剔”一个“为什么”的问题,因为规格说明书并没有说明任何特定功能的“为什么”。你不能要求规格说明书引用规范定义的内容。 - Nicol Bolas
我认为与常规函数的连贯性很重要,正如你所说的“仍然不允许抛出异常”,因此可以更直观地进行更改。 - Jarod42
2
为什么consteval函数应该是noexcept的?即使打破正交性,您将获得哪些好处? - Barry
1
@user7769147 这是正确的,但它并没有回答这个问题。 - Barry
显示剩余3条评论
1个回答

1
一些算法根据noexcept规范执行不同的操作(参见std::vector::resize())。此外,编译器可能会删除非抛出函数的异常处理代码。
立即函数在编译时调用。虽然C++20现在确实有编译时容器,但它们的性能与运行时代码有点无关紧要。而且他们可以很容易地使用不同的内部实现基于if(is_constant_evaluated),这将有益于不仅仅是noexcept查询。
但即使如此,constexpr编码的一个目标是使编译时代码像运行时代码一样。因此,如果您有一个应该只存在于编译时并具有consteval移动构造函数的类,则用户应该完全像运行时类一样思考它。因此,如果他们会在运行时类中使移动构造函数noexcept,则在编译时类中也应该是如此。
而且这是非常重要的,因为它保留了将来版本的语言中编译时代码能够抛出异常的能力。这尤其可能发生,如果P0709:静态异常进入标准。
此外,立即函数仅存在于编译时,这是一个没有异常处理的上下文。因此,编译器为构建constexpr函数的代码所做的任何操作都不涉及异常处理机制。因此,出于代码生成目的,将它们隐式地标记为noexcept是没有意义的。
最后,consteval最终只是从constexpr函数声明中进行了微小的更改。即使隐式inline也来自于consteval表示constexpr,而不是consteval本身。向consteval添加新的语义将是一个重大的变化。

它保留了编译时代码在将来能够抛出异常的能力,这些异常会导致编译时错误,无论如何它们都不会传播到函数外部。 "立即函数只存在于编译时,这是一个没有异常处理的上下文" 考虑 consteval int foo() {return 42; } void bar(int = foo()) noexcept; 在这种情况下,bar() 不是 noexcept! - user7769147
"那些异常会导致编译时错误,无论如何它们都不会传播出函数。" 我在谈论一个将来的语言变更,在该变更中,他们将取消编译时异常抛出的禁止。 - Nicol Bolas
2
@user7769147: "在这种情况下,bar() 不是 noexcept!" 是的,它是。noexcept 不会验证其中的代码是否可能导致异常被发出。它只是一个声明,该函数不会发出异常,如果尝试这样做,将调用 std::terminate - Nicol Bolas
“我在谈论一种未来的语言变化,他们将取消编译时异常抛出的禁令”,这根本没有任何意义, 在编译时上下文中抛出异常应该像使用 static_assert 一样,编译应该停止而不是在运行时处理异常。“在这种情况下,bar() 不是 noexcept!”“是的,你说得对,我的错。” - user7769147
1
@user7769147:并不是每个异常都意味着世界崩溃了,应用程序必须终止。异常可以是可恢复的状态,异常传播允许将该信息传递到其目的地,而无需有很多中间代码检查每个函数的返回值。 - Nicol Bolas
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接