`noexcept` behavior of `constexpr` functions

你。 提交于 2020-03-17 11:33:30

问题


The wording of [expr.unary.noexcept] changed in C++17 (edited following a comment by @L.F.: C++20).


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.


回答1:


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.



来源:https://stackoverflow.com/questions/60586265/noexcept-behavior-of-constexpr-functions

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!