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 if
statement ([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_assert
s 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