P0292R1 constexpr if has been included, on track for C++17. It seems useful (and can replace use of SFINAE), but a comment regarding static_assert being ill-formed, no diagnostic required in the false branch scares me:
Disarming static_assert declarations in the non-taken branch of a constexpr if is not proposed. void f() { if constexpr (false) static_assert(false); // ill-formed } template<class T> void g() { if constexpr (false) static_assert(false); // ill-formed; no // diagnostic required for template definition } I take it that it's completely forbidden to use static_assert inside constexpr if (at least the false / non-taken branch, but that in practice means it's not a safe or useful thing to do).
How does this come about from the standard text? I find no mentioning of static_assert in the proposal wording, and C++14 constexpr functions do allow static_assert (details at cppreference: constexpr).
Is it hiding in this new sentence (after 6.4.1) ? :
When a constexpr if statement appears in a templated entity, during an instantiation of the enclosing template or generic lambda, a discarded statement is not instantiated.
From there on, I assume that it is also forbidden, no diagnostic required, to call other constexpr (template) functions which somewhere down the call graph may call static_assert.
Bottom line:
If my understanding is correct, doesn't that put a quite hard limit on the safety and usefulness of constexpr if as we would have to know (from documentation or code inspection) about any use of static_assert? Are my worries misplaced?
Update:
This code compiles without warning (clang head 3.9.0) but is to my understanding ill-formed, no diagnostic required. Valid or not?
template< typename T> constexpr void other_library_foo(){ static_assert(std::is_same<T,int>::value); } template<class T> void g() { if constexpr (false) other_library_foo<T>(); } int main(){ g<float>(); g<int>(); }
Constexpr ifIf the value is true, then statement-false is discarded (if present), otherwise, statement-true is discarded.
Short answer: static_assert(false) should never appear in a constexpr if expression, regardless of whether it's in a template function or whether it's in the discarded branch.
This is talking about a well-established rule for templates - the same rule that allows compilers to diagnose template<class> void f() { return 1; }. [temp.res]/8 with the new change bolded:
The program is ill-formed, no diagnostic required, if:
- no valid specialization can be generated for a template or a substatement of a
constexpr ifstatement ([stmt.if]) within a template and the template is not instantiated, or- [...]
No valid specialization can be generated for a template containing static_assert whose condition is nondependent and evaluates to false, so the program is ill-formed NDR.
static_asserts with a dependent condition that can evaluate to true for at least one type are not affected.
C++20 makes static_assert in the else branch of if constexpr much shorter now, because it allows template lambda parameters. So to avoid the ill-formed case, we can now define a lambda with a bool template non-type parameter that we use to trigger the static_assert. We immediately invoke the lambda with (), but since the lambda won't be instantiated if its else branch is not taken, the assertion will not trigger unless that else is actually taken:
template<typename T> void g() { if constexpr (case_1) // ... else if constexpr (case_2) // ... else []<bool flag = false>() {static_assert(flag, "no match");}(); }
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