Jens Maurer <Jens.Maurer@gmx.net>
Audience: Core Working Group

P0292R2: constexpr if: A slightly different syntax


The paper P0128R1 "constexpr if" by Ville Voutilainen proposes a very useful facility to write compact template code that is instantiated depending on a compile-time condition. The motivation and rationale is sound and not repeated here.

This paper proposes a slightly different syntax, namely

  if constexpr(cond)
The differences vs. P0128R1 are: This syntax avoids seemingly disingenous repetitions of constexpr in if ... else if ... chains:
  // P0128R1
  constexpr if (cond)
  constexpr else constexpr if (cond)
  constexpr else constexpr if (cond)
  constexpr else
Compare with the syntax proposed in this paper:
  if constexpr (cond)
  else if constexpr (cond)
  else if constexpr (cond)

The proposed syntax was approved by EWG during the Jacksonville (2016-03) meeting of WG21.

The wording below incorporates initial feedback from CWG.

Not proposed

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

Changes compared to P0292R1 (see blue text)

Changes compared to P0292R0 (see blue text)


The wording is based on the wording presented in P0128R1 with modifications for the changes explained above.

Change in 3.2 [basic.def.odr] paragraph 4:

Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program outside of a discarded statement (6.4.1 [stmt.if]); no diagnostic required. The definition can appear explicitly in the program, it can be found in the standard or a user-defined library, or (when appropriate) it is implicitly defined (see 12.1, 12.4 and 12.8). An inline function shall be defined in every translation unit in which it is odr-used outside of a discarded statement.
Change in 6.4 [stmt.select] paragraph 1:
       if constexpropt ( condition ) statement
       if constexpropt ( condition ) statement else statement
       switch ( condition ) statement
See 8.3 [dcl.meaning] for the optional attribute-specifier-seq in a condition. In Clause 6, the term substatement refers to the contained statement or statements that appear in the syntax notation. ...
Add a new paragraph after 6.4.1 [stmt.if] paragraph 1:
If the parenthesized condition is prefixed with constexpr, the value of the condition shall be a contextually converted constant expression of type bool (5.20 [expr.const]); this form is called a constexpr if statement. If the value of the converted condition is false, the first substatement is a discarded statement, otherwise the second substatement, if present, is a discarded statement. During the instantation of an enclosing templated entity, if the condition is not value-dependent after its instantiation, the discarded substatement (if any) is not instantiated. [ Note: Odr-uses (3.2 [basic.def.odr]) in a discarded statement do not require an entity to be defined. -- end note ] A case or default label appearing within such an if statement shall be associated with a switch statement (6.4.2 [stmt.switch]) within the same if statement. A label (6.1 [stmt.label]) declared in a substatement of a constexpr if statement shall only be referred to by a statement (6.6.4 [stmt.goto]) in the same substatement. [ Example:
    template<typename T, typename ... Rest> void g(T&& p, Rest&& ...rs) {
      // ... handle p
      if constexpr (sizeof...(rs) > 0)
        g(rs...);  // never instantiated with an empty argument list.

    extern int x;   // no definition of x required
    int f() {
      if constexpr (true)
        return 0;
      else if (x)
        return x;
        return -x;
--- end example]
Change in [dcl.spec.auto] paragraph 2:
... If the declared return type of the function contains a placeholder type, the return type of the function is deduced from non-discarded return statements, if any, in the body of the function, if any (6.4.1 [stmt.if]).
Change in [dcl.spec.auto] paragraph 7:
When a variable declared using a placeholder type is initialized, or a non-discarded return statement occurs in a function declared with a return type that contains a placeholder type, the deduced return type or variable type is determined from the type of its initializer. ...
Change in [dcl.spec.auto] paragraphs 9-11:
If a function with a declared return type that contains a placeholder type has multiple non-discarded return statements, the return type is deduced for each return statement. If the type deduced is not the same in each deduction, the program is ill-formed.

If a function with a declared return type that uses a placeholder type has no non-discarded return statements, the return type is deduced as though from a return statement with no operand at the closing brace of the function body. [ Example: ... ]

Once a non-discarded return statement has been seen in a function, however, the return type deduced from that statement can be used in the rest of the function, including in other return statements. ...

Change in 14.5 [temp.decls] paragraph 2 as follows:
For purposes of name lookup and instantiation, default arguments and exception-specifications of function templates and default arguments and exception-specifications of member functions of class templates are considered definitions; each default argument or exception-specification is a separate definition which is unrelated to the function template definition or to any other default arguments or exception-specifications. For the purpose of instantiation, the substatements of a constexpr if statement (6.4.1 [stmt.if]) are considered definitions.
Change in 14.6 [temp.dep] paragraph 8:
... If no valid specialization can be generated for a template or a substatement of a constexpr if statement (6.4.1 [stmt.if]) within a template, and that the template is not instantiated, the template is ill-formed, no diagnostic required. ...
Change 14.7.1 [temp.inst] paragraph 11 as follows:
An implementation shall not implicitly instantiate a function template, a variable template, a member template, a non-virtual member function, a member class, or a static data member of a class template, or a substatement of a constexpr if statement (6.4.1 [stmt.if]) that does not require instantiation , unless such instantiation is required.


Thanks to Walter Brown for suggestions for wording improvement.