constexpr函数的noexcept行为 [英] `noexcept` behavior of `constexpr` functions

查看:452
本文介绍了constexpr函数的noexcept行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

[expr.unary.noexcept] 的措词在 C ++ 17 中已更改.


以前( n4140,5.3.7 noexcept运算符[expr.unary .noexcept] ),我的重点:

  1. 如果在可能评估的上下文中表达式包含以下内容,则noexcept运算符的结果为false

    (3.1)对函数,成员函数, 函数指针或不具有成员的成员函数指针 非抛出异常规范([except.spec]),除非调用 是一个常量表达式([expr.const]) ...


现在 1 ( 7.6.2.6 noexcept运算符[ expr.unary.noexcept] ):

  1. 除非表达式可能会被抛出([except.spec]),否则noexcept运算符的结果为 true .

然后在 14.5异常规范[except.spec] :

  1. 如果函数的声明中没有noexcept-specifier,则该声明中可能存在抛出异常的规范,除非...

,但14.5(3)中的除非列表并未列出constexpr,因此可能会抛出...

1 指向C ++ 17的链接):

true
false

使用 -std = c ++ 11 -std = c ++ 2a

编译时

但是现在可以打印出相同的代码(使用gcc 9.2的 ):

false
false

使用 -std = c ++ 11 -std = c ++ 2a

编译时

顺便说一下,lang语非常一致,从3.4.1起,并附带:

false
false


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

侧面说明: constexpr函数上的noexcept推论影响这个技巧 .

解决方案

摘要

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

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

规格上是否有真正的变化?如果是这样,发生此更改的原因是什么?

是的.请参阅下面的Clang错误报告中的引言.

如果规格中的更改影响或矛盾了过去 行为,强调改变和 它的含义?如果不强调更改,是否可以暗示它 可能是疏忽?

是;是的(但是CWG后来发现了理由来证明监督的合理性,因此将其保持原样).

如果这是真正的预期更改,那么它是否被视为可以解决的错误修复程序, 应该回到规范的先前版本,编译器正确吗 追溯使新行为与C ++ 11保持一致?

我不确定.请参阅下面的Clang错误报告中的引言.

详细信息

我搜索了很多地方,到目前为止,我能找到的最接近的地方是有关错误报告的评论:

  • GCC错误87603-[C ++ 17] noexcept is'不再是常量表达式的特殊情况了

    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对此进行了讨论,但决定保持其原样. C ++ 17的错误(故意使C ++ 14和C ++ 11失败). g ++, 但是,已经为C ++ 11实现了特殊情况,但尚未实现 对于C ++ 17进行更改.目前,icc和msvc的行为似乎类似于g ++.

  • C语错误15481-noexcept应该检查该表达式是否为常量表达式

    常量表达式特例已被wg21.link/p0003删除-显然是偶然的.我正在调查是否会 保持消失.

    您为避免在深层嵌套中出现二次运行采取了什么措施? 表情?

    [...]

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

    事实证明,这对于正确的库实际上是必不可少的 功能:例如,如果noexcept尝试评估其操作数,则 (例如)通过使std::swap中断is_nothrow_swappable constexpr,因为std::swap<T>然后常常会变得越来越 在T完成之前实例化.

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

换句话说, P0003 意外删除了该特殊规则,但是CWG决定保留该删除规则. /p>

The wording of [expr.unary.noexcept] changed in C++17.


Previously (n4140, 5.3.7 noexcept operator [expr.unary.noexcept]), my emphasis:

  1. 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]) ...


Now1 (7.6.2.6 noexcept operator [expr.unary.noexcept]):

  1. The result of the noexcept operator is true unless the expression is potentially-throwing ([except.spec]).

And then in 14.5 Exception specifications [except.spec]:

  1. If a declaration of a function does not have a noexcept-specifier, the declaration has a potentially throwing exception specification unless ...

but the unless list of 14.5(3) doesn't list constexpr, leaving it as potentially throwing...

1 a link to C++17 n4659 added by L.F. in a comment.


Test code

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;

used to print (with gcc 8.3):

true
false

both when compiled with -std=c++11 and -std=c++2a


However the same code prints now (with gcc 9.2):

false
false

both when compiled with -std=c++11 and -std=c++2a


Clang by the way is very consistent, since 3.4.1 and goes with:

false
false


  • What is the right behavior per each spec?
  • Was there a real change in the spec? If so, what is the reason for this change?
  • 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?
  • 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?

Side Note: the noexcept deduction on a constexpr function affects this trick.

解决方案

Summary

What is the right behavior per each spec?

true false before C++17, false false since C++17.

Was there a real change in the spec? If so, what is the reason for this change?

Yes. See the quote from the Clang bug report below.

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?

Yes; yes (but CWG found a reason to justify the oversight later, so it was kept as-is).

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?

I'm not sure. See the quote from the Clang bug report below.

Detail

I have searched many places, and so far the closest thing I can find is the comments on relevant bug reports:

  • 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.

In other words, the special rule was accidentally removed by P0003, but CWG decided to keep the removal.

这篇关于constexpr函数的noexcept行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆