P0292R0: constexpr if: A slightly different syntax

Introduction

This paper proposes a slightly different syntax, namely

if constexpr(cond) statement1; else statement2;

constexpr is placed before the condition, indicating that the condition is evaluated as a compile-time constant.

is placed before the condition, indicating that the condition is evaluated as a compile-time constant. constexpr is not repeated before or after the else ; an else binds to the nearest preceding if-statement as before (regardless of constexpr )

is not repeated before or after the ; an binds to the nearest preceding if-statement as before (regardless of ) a constexpr if is valid in a non-template and suppresses requirements for entity definitions in the non-taken branch

return statements in a non-taken branch are ignored for return type deduction

// P0128R1 constexpr if (cond) statement1; constexpr else constexpr if (cond) statement2; constexpr else constexpr if (cond) statement3; constexpr else statement4;

if constexpr (cond) statement1; else if constexpr (cond) statement2; else if constexpr (cond) statement3; else statement4;

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

static_assert

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 }

Wording

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 .

selection-statement: if constexpr opt ( condition ) statement if constexpr opt ( 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. ...

If the parenthesized condition is prefixed with constexpr , 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. 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 discarded statement shall not be referred to by a statement (6.6.4 [stmt.goto]) outside of a discarded statement. 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. [ 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; else return -x; } --- end example] [ Note: Odr-uses (3.2 [basic.def.odr]) in a discarded statement do not require an entity to be defined. -- end note ]

... 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 in the body of the function, if any (6.4.1 [stmt.if]) .

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. ...

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. ...

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.

... 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. ...