Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

constexpr if and static_assert

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>(); } 
like image 701
Johan Lundberg Avatar asked Jul 11 '16 10:07

Johan Lundberg


People also ask

What is if constexpr?

Constexpr ifIf the value is true, then statement-false is discarded (if present), otherwise, statement-true is discarded.

Which is false about constexpr?

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.


Video Answer


2 Answers

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_asserts with a dependent condition that can evaluate to true for at least one type are not affected.

like image 64
T.C. Avatar answered Sep 23 '22 01:09

T.C.


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");}(); } 
like image 20
Nikos C. Avatar answered Sep 26 '22 01:09

Nikos C.