The wording of [expr.unary.noexcept] changed in C++17.
Previously (n4140, 5.3.7 noexcept operator [expr.unary.noexcept]), my emphasis:
- 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]):
- 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]:
- 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.
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
Side Note: the noexcept
deduction on a constexpr
function affects this trick.
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.
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 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.
In other words, the special rule was accidentally removed by P0003, but CWG decided to keep the removal.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With