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.
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.
Related
Take the following code:
template <typename T, typename U>
constexpr bool can_represent(U&& w) noexcept
{
return [] (auto&& x) {
try {
return T(std::forward<U>(x)) == std::forward<U>(x);
} catch(...) {
return false;
}
} (std::forward<U>(w));
}
I am using this function in a constant expression (template).
gcc compiles it without a problem. clang and MSVC don't, lamenting that the function does not result in a constant expression.
Indeed, gcc did not immediately accept this either; it was getting hung up on the try, that normally wouldn't be allowed in a constexpr function. That's why I had to use an immediately invoked lambda expression. However, now it works, and considering it only works with gcc I'm quite confused.
Which compiler is correct?
Is there a property of the lambda that permits this to work in a constexpr context, or is this some kind of non-standard gcc extension?
[I've used godbolt to compile with clang and MSVC, where as I have gcc 8.1.0 on my machine]
[gcc] was getting hung up on the try, that normally wouldn't be allowed in a constexpr function.
This is correct for a C++17 program. (C++20 relaxed this, so a try block can now be used in a constexpr function. However, it is only the try that is allowed; it is not allowed for execution to hit something that throws an exception.)
That's why I had to use an immediately invoked lambda expression.
The implication here is that your approach made your code valid. This is incorrect. Using an an immediately invoked lambda did not work around the problem; it swept the problem under the rug. The try is still a problem, but now compilers do not have to tell you it is a problem.
Using a lambda switches the constexpr criterion from the straight-forward "the function body must not contain a try-block" to the indirect "there exists at least one set of argument values such that an invocation of the function could be an evaluated subexpression of a core constant expression". The tricky part here is that a violation of the latter criterion is "no diagnostic required", meaning that all the compilers are correct, whether or not they complain about this code. Hence my characterization of this as sweeping the problem under the rug.
So why is... that criterion is a long thing to repeat... what's the issue involving "core constant expressions"? C++17 removed the prohibition against lambdas in core constant expressions, so that much looks good. However, there is still a requirement that all function calls within the constexpr function also be themselves constexpr. Lambdas can become constexpr in two ways. First, they can be explicitly marked constexpr (but if you do that here, the complaint about the try block should come back). Second, they can simply satisfy the constexpr function requirements. However, your lambda contains a try, so it is not constexpr (in C++17).
Your lambda is not a valid constexpr function. Hence calling it is not allowed in a core constant expression. There is no execution path through can_represent() that avoids invoking your lambda. Therefore, can_represent is not a valid constexpr function, no diagnostic required.
The [[nodiscard]] attribute introduced in C++17 standard, and in case of the
... potentially-evaluated discarded-value expression,..., implementations are encouraged to issue a warning in such cases.
Source: n4659, C++17 final working draft.
Similar phrasing is used on cppreference, that in case of "violation":
the compiler is encouraged to issue a warning.
Why is the word encouraged used instead of required? Are there situations (except, the explicit cast to void) when a compiler is better off not issuing a warning? What is the reason behind softening the standard language in the particular case of relatively safe requirement to issue a warning no matter what (again, except, say, explicit cast to void)?
The C++ standard specifies the behavior of a valid C++ program. In so doing, it also defines what "valid C++ program" means.
Diagnostics are only required for code which is ill-formed, code which is syntactically or semantically incorrect (and even then, there are some ill-formed circumstances that don't require diagnostics). Either the code is well-formed, or it is ill-formed and (usually) a diagnostic is displayed.
So the very idea of a "warning" is just not something the C++ standard recognizes, or is meant to recognize. Notice that even the "implementations are encouraged to issue a warning" statement is in a non-normative notation, rather than a legitimate specification of behavior.
In C++11, it is easy to SFINAE on whether or not an expression is valid. As an example, imagine checking if something is streamable:
template <typename T>
auto print_if_possible(std::ostream& os, const T& x)
-> decltype(os << x, void());
print_if_possible will only participate in overload resolution if os << x is a well-formed expression.
live example on godbolt.org
I need to do the same in C++03, and I figured out that sizeof could help (as I needed an unevaluated context for an expression). This is what I came up with:
template <int> struct sfinaer { };
template <typename T>
void print_if_possible(std::ostream& os, const T& x,
sfinaer<sizeof(os << x)>* = NULL);
live example on godbolt.org
It seems that both the latest versions of g++ and clang++ accept the sizeof version with -std=c++03 -Wall -Wextra.
Is the code guaranteed to work as intended in C++03?
Is it correct to conclude that any usage of C++11 expression SFINAE can be backported to C++03 using sfinaer and sizeof?
Expression SFINAE is a bit gray. C++03 basically said nothing on the subject. It neither explicitly banned it nor explicitly allowed it. Contemporary implementations did not permit such constructs because it caused substantial implementation complexity and it's unclear whether it's meant to be allowed, and CWG was at one point leaning towards banning it (see the April, 2003 note) before it eventually reversed course, partially in light of decltype and constexpr that were added to C++11 (see the introduction to N2634).
This also all happened well before CWG started explicitly marking the DR status of issues whose resolutions are meant to apply retroactively.
I think the best advice here is simply "ask your compiler vendor". A compiler that supports expression SFINAE in its C++11 mode is unlikely to tear out that support in C++03 mode (the vendor may treat CWG 339 as a defect report and apply it retroactively, or consider it as an extension). OTOH, a compiler that never supported C++11 is unlikely to invest the substantial costs necessary for expression SFINAE to work (indeed, it did not work in a certain major compiler cough until relatively recently). I also suspect that a place still stuck with an 15-year-old language is unlikely to use the modern toolchains necessary for such support.
What is the rationale for [dcl.constexpr]p5 (http://eel.is/c++draft/dcl.constexpr#5)?
For a non-template, non-defaulted constexpr function or a
non-template, non-defaulted, non-inheriting constexpr constructor, if
no argument values exist such that an invocation of the function or
constructor could be an evaluated subexpression of a core constant
expression ([expr.const]), or, for a constructor, a constant
initializer for some object ([basic.start.init]), the program is
ill-formed; no diagnostic required.
If a program violated this rule, declaring the offending function constexpr was useless. So what? Isn't it better to accept useless uses of the decl-specifier constexpr instead of triggering undefined behaviour (by no diagnostics required)? In addition to the problem with undefined behaviour we also have the additional complexity of having the rule [dcl.constexpr]p5 in the standard.
An implementation can still provide useful diagnostic messages in some cases that it is able to detect (warnings by convention). Just like in the following case:
int main() { 0; }
The expression in main there is well-formed but useless. Some compilers issue a diagnostic message anyway (and they are allowed to) in the form of a warning.
I understand that [dcl.constexpr]p5 cannot require diagnostics, so i'm not asking about that. I'm just asking about why this rule is even in the standard.
The reason it's ill-formed is because making it ill-formed allows implementations to reject constexpr function definitions that cannot possibly form constant expressions. Rejecting them early means getting more useful diagnostics.
The reason no diagnostic is required is because it may be unrealistic for an implementation to determine that for each and every possible combination of arguments, the result is not a constant expression.
The fact that ill-formed, no diagnostic required, effectively means the same thing as making the behaviour undefined seems to me as if it's unfortunate, but merely picked for lack of a better option. I'd be highly surprised if the intent would actually be to allow any arbitrary run-time behaviour, but there is no concept of "may be diagnosed as an error, but if not, must behave as specified" for any language feature in C++.
gcc compiles the following code without warning:
#include <cmath>
struct foo {
static constexpr double a = std::cos(3.);
static constexpr double c = std::exp(3.);
static constexpr double d = std::log(3.);
static constexpr double e1 = std::asin(1.);
static constexpr double h = std::sqrt(.1);
static constexpr double p = std::pow(1.3,-0.75);
};
int main()
{
}
None of the standard library functions used above are constexpr functions, we are allowed to use them where a constant expression is required from both the draft C++11 standard and draft C++14 standard section 7.1.5 [dcl.constexpr]:
[...]If it is initialized by a constructor call, that call shall be a
constant expression (5.19). Otherwise, or if a constexpr specifier is
used in a reference declaration, every full expression that appears in
its initializer shall be a constant expression.[...]
Even when using -std=c++14 -pedantic or -std=c++11 -pedantic no warnings are generated (see it live). Using -fno-builtin produces errors (see it live) which indicates that the builtin version of these standard library functions are being treated as if they where constexpr
While clang does not allow the code with any combination of flags I have tried.
So this is a gcc extension to treat at least some builtin functions as if they were constexpr functions even though the standard does not explicitly require them to be. I would have expected to at least receive a warning in strict conformance mode, is this a conforming extension?
TL;DR
In C++14 this is explicitly not allowed, although in 2011 it appeared like this case would be explicitly allowed. It is unclear if for C++11 this fell under the as-if rule, I don't believe it does since it alters observable behavior but that point was not clarified in the issue I reference below.
Details
The answer to this question has shifted with the evolving status of LWG issue 2013 which opens with:
Suppose that a particular function is not tagged as constexpr in the
standard, but that, in some particular implementation, it is possible
to write it within the constexpr constraints. If an implementer tags
such a function as constexpr, is that a violation of the standard or
is it a conforming extension?
In C++11 it was unclear if the as-if rule permitted this but the orignal proposal would have explicitly allowed it once it was accepted and we can see below in the gcc bug report I reference, this was the assumption made by the gcc team.
The consensus to allow this shifted in 2012 and the proposal changed and in C++14 this is a non-conforming extension. This is reflected in the draft C++14 standard section 17.6.5.6 [constexpr.functions] which says:
[...]An implementation shall not declare any standard library function
signature as constexpr except for those where it is explicitly
required.[..]
and although a strict reading of this seems to leave some wiggle room for treating a builtin implicitly as if it were a constexpr we can see from the following quote in the issue that the intention was to prevent divergence in implementations since identical code could produce different behavior when using SFINAE (emphasis mine):
Some concern expressed when presented to full committee for the vote
to WP status that this issue had been resolved without sufficient
thought of the consequences for diverging library implementations, as
users may use SFINAE to observe different behavior from otherwise
identical code.
We can see from the gcc bug report [C++0x] sinh vs asinh vs constexpr that the team relied on the earlier proposed resolution of LWG 2013 which says:
[...]Additionally, an implementation may declare any function to be
constexpr if that function's definition satisfies the necessary
constraints[...]
when deciding whether this change for the math functions was allowed in strict conformance mode.
As far as I can tell this would become conforming if this we received a warning in strict conformance mode i.e. using -std=c++11 -pedantic or if it was disabled in this mode.
Note, I added a comment to the bug report explaining that the resolution changed since this issue was originally addressed.
Jonathan Wakely pointed out in another question a more recent discussion and it seems likely the gcc bug report will be reopened to address this conformance issue.
What about intrinsics
Compiler intrinsics are not covered by the standard and so as far as I can tell they should be exempt from this rule, so using:
static constexpr double a = __builtin_cos(3.);
should be allowed. This question came up in the bug report and opinion of Daniel Krügler was:
[...]Library functions and other intrinsics can probably be considered as
exceptions, because they are not required to be "explainable" by
normal language rules.