`constexpr` 函数的 `noexcept` 行为
`noexcept` behavior of `constexpr` functions
[expr.unary.noexcept] 的措辞在 C++17.
中发生了变化
之前(n4140, 5.3.7 noexcept operator [expr.unary.noexcept]),我的重点:
- The result of the noexcept operator is false if in a potentially-evaluated context the expression would contain
(3.1) a potentially-evaluated call to a function, member function,
function pointer, or member function pointer that does not have a
non-throwing exception-specification ([except.spec]), unless the call
is a constant expression ([expr.const]) ...
现在1 (7.6.2.6 noexcept operator [expr.unary.noexcept]):
- The result of the noexcept operator is true unless the expression is potentially-throwing ([except.spec]).
然后在14.5 Exception specifications [except.spec]:
- If a declaration of a function does not have a noexcept-specifier, the declaration has a potentially throwing exception specification unless ...
但是 14.5(3) 的 unless list 没有列出 constexpr
,因此可能会抛出...
1 a link 到 C++17 n4659 添加 L.F。在评论中。
测试代码
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;
用于打印 (with gcc 8.3):
true
false
当使用 -std=c++11 和 -std=c++2a
编译时
但是 现在打印相同的代码 (with gcc 9.2):
false
false
当使用 -std=c++11 和 -std=c++2a
编译时
顺便说一下,Clang 非常一致,since 3.4.1 并且符合:
false
false
- 每个规范的正确行为是什么?
- 规范有真正的变化吗?如果是这样,这种变化的原因是什么?
- 如果规范中有影响或与过去行为相矛盾的变化,强调这种变化及其影响是否是一种常见的做法?如果更改未被强调,是否暗示它可能是疏忽?
- 如果这是一个真正有意的改变,它是否被认为是一个错误修复,应该回到规范的以前版本,编译器是否正确地将新行为追溯至 C++11?
旁注: constexpr
函数的 noexcept
推导 影响 this trick.
总结
What is the right behavior per each spec?
true false
在 C++17 之前,false false
自 C++17 之后。
Was there a real change in the spec? If so, what is the reason for this change?
是的。请参阅下面的 Clang 错误报告中的引述。
If there is a change in the spec that affects or contradicts past
behavior, would it be a common practice to emphasize that change and
its implications? If the change is not emphasized can it imply that it
might be an oversight?
是;是的(但 CWG 后来找到了证明疏忽合理的理由,所以它保持原样)。
If this is a real intended change, was it considered a bug fix that
should go back to previous versions of the spec, are compilers right
with aligning the new behavior retroactively to C++11?
我不确定。请参阅下面的 Clang 错误报告中的引述。
详情
我搜索了很多地方,到目前为止我能找到的最接近的是相关错误报告的评论:
GCC Bug 87603 - [C++17] noexcept isn't special cased for constant expressions anymore
CWG 1129 (which ended up in C++11) added a special case to noexcept
for constant expressions, so that:
constexpr void f() {} static_assert(noexcept(f()));
CWG 1351 (which ended up in C++14) changed the wording significantly,
but the special case remained, in a different form.
P0003R5 (which ended up in C++17) changed the wording again, but the
special case was removed (by accident), so now:
constexpr void f() {} static_assert(!noexcept(f()));
According to Richard Smith in LLVM 15481, CWG discussed this but decided to keep the behavior as-is. Currently, clang does the right
thing for C++17 (and fails for C++14 and C++11, on purpose). g++,
however, implemented the special case for C++11 already, but not the
change for C++17. Currently, icc and msvc seem to behave like g++.
Clang Bug 15481 - noexcept should check whether the expression is a constant expression
The constant expression special case was removed -- apparently by accident -- by wg21.link/p0003. I'm investigating whether it's going
to stay gone or not.
Did you do anything to avoid quadratic runtime on deeply-nested
expressions?
[...]
Conclusion from CWG discussion: we're going to keep this as-is. noexcept
has no special rule for constant expressions.
It turns out this is actually essential for proper library
functionality: e.g., if noexcept
tries evaluating its operand, then
(for example) is_nothrow_swappable
is broken by making std::swap
constexpr
, because std::swap<T>
then often ends up getting
instantiated before T
is complete.
As a result of that, I'm also going to consider this change as an
effective DR against C++11 and C++14... but I'm open to reconsidering
if we see many user complaints.
换句话说,特殊规则被 P0003 意外删除,但 CWG 决定保留删除。
[expr.unary.noexcept] 的措辞在 C++17.
中发生了变化之前(n4140, 5.3.7 noexcept operator [expr.unary.noexcept]),我的重点:
- The result of the noexcept operator is false if in a potentially-evaluated context the expression would contain
(3.1) a potentially-evaluated call to a function, member function, function pointer, or member function pointer that does not have a non-throwing exception-specification ([except.spec]), unless the call is a constant expression ([expr.const]) ...
现在1 (7.6.2.6 noexcept operator [expr.unary.noexcept]):
- The result of the noexcept operator is true unless the expression is potentially-throwing ([except.spec]).
然后在14.5 Exception specifications [except.spec]:
- If a declaration of a function does not have a noexcept-specifier, the declaration has a potentially throwing exception specification unless ...
但是 14.5(3) 的 unless list 没有列出 constexpr
,因此可能会抛出...
1 a link 到 C++17 n4659 添加 L.F。在评论中。
测试代码
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;
用于打印 (with gcc 8.3):
true
false
当使用 -std=c++11 和 -std=c++2a
编译时但是 现在打印相同的代码 (with gcc 9.2):
false
false
当使用 -std=c++11 和 -std=c++2a
编译时顺便说一下,Clang 非常一致,since 3.4.1 并且符合:
false
false
- 每个规范的正确行为是什么?
- 规范有真正的变化吗?如果是这样,这种变化的原因是什么?
- 如果规范中有影响或与过去行为相矛盾的变化,强调这种变化及其影响是否是一种常见的做法?如果更改未被强调,是否暗示它可能是疏忽?
- 如果这是一个真正有意的改变,它是否被认为是一个错误修复,应该回到规范的以前版本,编译器是否正确地将新行为追溯至 C++11?
旁注: constexpr
函数的 noexcept
推导 影响 this trick.
总结
What is the right behavior per each spec?
true false
在 C++17 之前,false false
自 C++17 之后。
Was there a real change in the spec? If so, what is the reason for this change?
是的。请参阅下面的 Clang 错误报告中的引述。
If there is a change in the spec that affects or contradicts past behavior, would it be a common practice to emphasize that change and its implications? If the change is not emphasized can it imply that it might be an oversight?
是;是的(但 CWG 后来找到了证明疏忽合理的理由,所以它保持原样)。
If this is a real intended change, was it considered a bug fix that should go back to previous versions of the spec, are compilers right with aligning the new behavior retroactively to C++11?
我不确定。请参阅下面的 Clang 错误报告中的引述。
详情
我搜索了很多地方,到目前为止我能找到的最接近的是相关错误报告的评论:
GCC Bug 87603 - [C++17] noexcept isn't special cased for constant expressions anymore
CWG 1129 (which ended up in C++11) added a special case to
noexcept
for constant expressions, so that:constexpr void f() {} static_assert(noexcept(f()));
CWG 1351 (which ended up in C++14) changed the wording significantly, but the special case remained, in a different form.
P0003R5 (which ended up in C++17) changed the wording again, but the special case was removed (by accident), so now:
constexpr void f() {} static_assert(!noexcept(f()));
According to Richard Smith in LLVM 15481, CWG discussed this but decided to keep the behavior as-is. Currently, clang does the right thing for C++17 (and fails for C++14 and C++11, on purpose). g++, however, implemented the special case for C++11 already, but not the change for C++17. Currently, icc and msvc seem to behave like g++.
Clang Bug 15481 - noexcept should check whether the expression is a constant expression
The constant expression special case was removed -- apparently by accident -- by wg21.link/p0003. I'm investigating whether it's going to stay gone or not.
Did you do anything to avoid quadratic runtime on deeply-nested expressions?
[...]
Conclusion from CWG discussion: we're going to keep this as-is.
noexcept
has no special rule for constant expressions.It turns out this is actually essential for proper library functionality: e.g., if
noexcept
tries evaluating its operand, then (for example)is_nothrow_swappable
is broken by makingstd::swap
constexpr
, becausestd::swap<T>
then often ends up getting instantiated beforeT
is complete.As a result of that, I'm also going to consider this change as an effective DR against C++11 and C++14... but I'm open to reconsidering if we see many user complaints.
换句话说,特殊规则被 P0003 意外删除,但 CWG 决定保留删除。