Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

if constexpr is a compile-time conditional statement introduced in C++17 that instructs the compiler to evaluate a boolean constant expression and discard the unselected branch from the final instantiated code. Unlike a standard if statement, the discarded branch does not participate in template instantiation, return type deduction, or runtime execution.

Syntax

if constexpr (condition) {
    // statement-true
} else if constexpr (condition) {
    // statement-true
} else {
    // statement-false
}
The condition must be a contextually converted constant expression of type bool. It must be evaluable entirely at compile time.

Compiler Mechanics and Discarded Statements

When the compiler evaluates an if constexpr statement, the branch that evaluates to false becomes a discarded statement. The compiler enforces specific rules regarding how these discarded statements are processed:

1. Template Instantiation

In templated contexts, if the condition depends on a template parameter, the discarded branch is not instantiated. This means code within the discarded branch that would normally be ill-formed for a specific template type will not trigger a compilation error, provided the error depends on the template parameter.
template <typename T>
void process(T value) {
    if constexpr (std::is_pointer_v<T>) {
        // If T is int, this branch is discarded and NOT instantiated.
        // Therefore, *value does not cause a compilation error.
        *value = 10; 
    } else {
        // If T is int*, this branch is discarded.
        value = 10;
    }
}

2. Non-Dependent Well-Formedness

A discarded statement is not completely ignored by the parser. It must still be syntactically valid. Furthermore, if the code inside the discarded branch does not depend on a template parameter, it must be semantically well-formed, even if it is discarded.
template <typename T>
void check() {
    if constexpr (sizeof(T) > 4) {
        // Valid: Depends on T. Ignored if sizeof(T) <= 4.
        T::invalid_function(); 
    } else {
        // ERROR: Ill-formed. Does not depend on T. 
        // The compiler will flag this even if the branch is discarded.
        undeclared_global_function(); 
    }
}

3. Return Type Deduction

When a function uses auto for return type deduction, return statements inside discarded branches do not participate in deducing the function’s return type.
template <typename T>
auto getValue(T t) {
    if constexpr (std::is_integral_v<T>) {
        return t; // If T is int, return type is deduced as int
    } else {
        return 0.0; // If T is int, this is discarded and does not conflict with 'int'
    }
}
If a standard if were used in the example above, the compiler would throw an error for inconsistent deduced return types (int vs double). if constexpr prevents this by eliminating the unselected return statement before type deduction occurs.

4. Initialization Statements

Like standard if statements (since C++17), if constexpr supports initialization statements. The initialized variable is visible in both the true and false branches, but its type and value must be resolvable at compile time if used within the constant expression condition.
if constexpr (constexpr int x = sizeof(int); x > 2) {
    // x is available here
} else {
    // x is also available here
}

5. No Short-Circuiting of Template Substitution

When multiple conditions are chained using logical operators (&&, ||) within an if constexpr condition, the evaluation of the constant expression short-circuits, but the substitution of template parameters into the entire condition occurs before evaluation. Consequently, substituting an invalid type into the right-hand side of a logical operator causes a hard compilation error (not SFINAE), even if the left-hand side dictates that the right-hand side would not be evaluated. To achieve true short-circuiting that prevents invalid template substitutions, nested if constexpr blocks are required.
template <typename T>
void evaluate_type() {
    // ERROR: If T does not have a nested 'value' member, substitution fails 
    // and causes a hard error, even if has_value_v<T> is false.
    /* 
    if constexpr (has_value_v<T> && T::value > 0) { 
        // ...
    } 
    */

    // CORRECT: The nested 'if constexpr' ensures substitution into T::value 
    // only occurs if the first condition is true.
    if constexpr (has_value_v<T>) {
        if constexpr (T::value > 0) {
            // ...
        }
    }
}
Master C++ with Deep Grasping Methodology!Learn More