“constexpr”函数的“noexcept”行为

2024-04-19

的措辞[expr.unary.noexcept]改变于C++17.


之前 (n4140, 5.3.7 noexcept 运算符 [expr.unary.noexcept] https://timsong-cpp.github.io/cppwp/n4140/expr.unary.noexcept), 我的重点:

  1. The result of the noexcept operator is false if in a potentially-evaluated context the expression would contain

    (3.1) 对函数、成员函数的潜在评估调用, 函数指针,或没有成员函数指针 非抛出异常规范([except.spec]),除非打电话 是一个常量表达式 ([expr.const]) ...


Now1 (7.6.2.6 noexcept operator [expr.unary.noexcept] https://eel.is/c++draft/expr.unary.noexcept):

  1. noexcept 运算符的结果是true除非表达式可能抛出异常([ except.spec] )。

然后在14.5 异常规范[ except.spec ] https://eel.is/c++draft/except.spec:

  1. 如果函数的声明没有 noexcept 说明符,则该声明具有可能引发异常的说明,除非...

but the 除非列出14.5(3) 未列出constexpr,将其保留为可能抛出...

1 a link to C++17 n4659 https://timsong-cpp.github.io/cppwp/n4659/except.spec#6 added by L.F. in a comment.


测试代码

constexpr int f(int i) { return i; }

std::cout << boolalpha << noexcept(f(7)) << std::endl;
int a = 7;
std::cout << boolalpha << noexcept(f(a)) << std::endl;

用于打印(与海湾合作委员会8.3 https://godbolt.org/z/ivAnRa):

true
false

两者都在编译时使用-std=c++11 and -std=c++2a


However现在打印相同的代码(与海湾合作委员会9.2 https://godbolt.org/z/GWUEdg):

false
false

两者都在编译时使用-std=c++11 and -std=c++2a


顺便说一句,Clang 非常一致,从3.4.1开始 https://godbolt.org/z/UsBDok并与:

false
false

  • 每个规范的正确行为是什么?
  • 规格有真正的变化吗?如果是这样,这种变化的原因是什么?
  • 如果规范中的更改影响或与过去的行为相矛盾,那么常见的做法是强调这种变化及其影响?如果改变是没有强调这是否意味着它可能是监督?
  • 如果这是真正有意的更改,是否认为它是一个错误修复,应该追溯到规范的先前版本,编译器是否正确地将新行为追溯至 C++11?

Side Note: the noexcept deduction on a constexpr function affects this trick https://stackoverflow.com/a/35110453/2085626.


Summary

每个规范的正确行为是什么?

true false在 C++17 之前,false false从 C++17 开始。

规格有真正的变化吗?如果是这样,这种变化的原因是什么?

是的。请参阅下面 Clang 错误报告中的引用。

如果规范发生影响或与过去相矛盾的变化 行为,强调改变和 它的影响是什么?如果不强调这一变化是否意味着它 可能是一个疏忽?

是的;是的(但 CWG 后来找到了一个理由来证明这一监督是合理的,因此保持原样)。

如果这是真正有意的更改,是否被视为错误修复 应该回到规范的早期版本,编译器是否正确 将新行为追溯至 C++11?

我不知道。请参阅下面 Clang 错误报告中的引用。

Detail

我找了很多地方,目前能找到的最接近的就是相关bug报告的评论:

  • GCC Bug 87603 - [C++17] noexcept 不再是常量表达式的特殊情况 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87603

    CWG 1129(最终以 C++11 形式出现)添加了一个特殊情况noexcept对于常量表达式,因此:

    constexpr void f() {} static_assert(noexcept(f()));
    

    CWG 1351(最终采用 C++14)显着改变了措辞, 但特殊情况仍然存在,只是形式不同。

    P0003R5(最终出现在 C++17 中)再次更改了措辞,但是 特殊情况被删除(意外),所以现在:

    constexpr void f() {} static_assert(!noexcept(f()));
    

    根据 LLVM 15481 中的 Richard Smith 的说法,CWG 对此进行了讨论,但决定保持该行为不变。目前,clang 做得对 对于 C++17 来说是这样的(对于 C++14 和 C++11 来说是故意失败的)。克++, 然而,已经实现了 C++11 的特殊情况,但还没有实现 C++17 的更改。目前,icc 和 msvc 的行为似乎与 g++ 类似。

  • Clang Bug 15481 - noexcept 应该检查表达式是否是常量表达式 https://bugs.llvm.org/show_bug.cgi?id=15481

    常量表达式特殊情况被 wg21.link/p0003 删除了——显然是偶然的。我正在调查是否会进行 是否离开。

    您是否采取了任何措施来避免深度嵌套的二次运行时 表达式?

    [...]

    CWG 讨论的结论:我们将保持原样。noexcept对于常量表达式没有特殊的规则。

    事实证明这对于正确的图书馆来说实际上是必不可少的 功能:例如,如果noexcept尝试评估其操作数,然后 (例如)is_nothrow_swappable因制造而被打破std::swap constexpr, 因为std::swap<T>然后经常会得到 之前实例化过T做完了。

    因此,我还将将此更改视为 针对 C++11 和 C++14 的有效 DR...但我愿意重新考虑 如果我们看到很多用户投诉。

换句话说,特殊规则被意外地删除了P0003 https://wg21.link/p0003,但 CWG 决定保留删除。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

“constexpr”函数的“noexcept”行为 的相关文章

随机推荐