Ch7.3: if constexpr

Overview

if constexpr is a compile‑time conditional. When its condition is a constant expression, the compiler removes the branch that is not taken. This is different from a normal if, which always generates both branches in the program.

This chapter shows how if constexpr works, how to chain else if constexpr, how it can replace certain uses of #ifdef, and how it helps prevent hidden syntax errors.

1. Basic example

If the condition is a constant expression, the compiler keeps only the selected branch.


constexpr bool use_fast_path = true;

int compute(int x)
{
    if constexpr(use_fast_path)
    {
        return x * 2;   // this branch is kept
    }
    else
    {
        return x + 2;   // this branch is removed
    }
}

Because use_fast_path is constexpr, the compiler discards the else branch entirely.

2. Why this matters

if constexpr is useful when you want to:

Unlike a normal if, the discarded branch is not compiled at all.

3. if constexpr / else if constexpr / else

You can chain if constexpr with else if constexpr and else. Only one branch is selected, and all others are removed at compile time.


#include <fast_io.h>

constexpr int mode = 2;

void handle_mode()
{
    using namespace ::fast_io::iomnp;

    if constexpr(mode == 1)
    {
        print("mode 1\n");
    }
    else if constexpr(mode == 2)
    {
        print("mode 2\n");
    }
    else
    {
        print("other mode\n");
    }
}

Only the matching branch is kept in the final program.

4. Using it with constexpr variables

A common pattern is to control behavior using a constexpr configuration variable.


#include <fast_io.h>

constexpr bool print_hex = false;

void print_value(int x)
{
    using namespace ::fast_io::iomnp;

    if constexpr(print_hex)
        print("hex: ", hex(x), "\n");
    else
        print("dec: ", x, "\n");
}

If print_hex is false, the compiler removes the entire hex printing branch.

5. Replacing some #ifdef usage

if constexpr is very useful when combined with macros. In many cases, it can replace #ifdef inside functions. The idea is to convert a macro into a constexpr boolean, and then use if constexpr to remove unused branches at compile time.


#include <fast_io.h>

void foo()
{
    using namespace ::fast_io::iomnp;

    constexpr bool is_windows{
    #ifdef _WIN32
        true
    #else
        false
    #endif
    };

    if constexpr(is_windows)
    {
        win32_foo();
    }
    else
    {
        posix_foo();
    }
}

This keeps the function readable and avoids scattering #ifdef throughout the code.

6. Why this is safer than large #ifdef blocks

#ifdef removes code before the compiler ever sees it. This can hide syntax errors or invalid code inside the disabled branch.

For example:


#ifdef _WIN32
    win32_foo();
#else
    foo()   // missing semicolon, but never compiled on Windows
#endif

On Windows, the foo() line is removed by the preprocessor, so the compiler never checks it. The error remains hidden until someone builds on a different platform.

With if constexpr, both branches must be valid C++. The compiler parses both, then discards the unused one only after verifying it.


#include <fast_io.h>

void foo()
{
    using namespace ::fast_io::iomnp;

    constexpr bool is_windows{
    #ifdef _WIN32
        true
    #else
        false
    #endif
    };

    if constexpr(is_windows)
        win32_foo();
    else
        foo(); // must be valid C++ or compilation fails
}

This reduces the chance of hidden compilation failures.

7. Using if constexpr(false) to disable code

if constexpr(false) can be used to temporarily disable a block of code. The disabled branch is removed at compile time, but the compiler still checks it for correctness.


if constexpr(false)
{
    do_something();   // must still be valid C++
}

This is different from:


#if 0
    do_something()    // missing semicolon, but never checked
#endif

#if 0 is still useful when the programmer intentionally wants to hide unfinished or syntactically invalid code. In that case, the goal is to prevent the compiler from seeing the code at all.

But when you want the compiler to keep checking the disabled code for correctness, if constexpr(false) is the safer choice.

8. Checking endianness with if constexpr

if constexpr is also useful for selecting code paths based on compile‑time properties of the platform, such as endianness.


#include <bit>
#include <fast_io.h>

void show_endianness()
{
    using namespace ::fast_io::iomnp;

    if constexpr(::std::endian::little == ::std::endian::native)
    {
        print("native endianness: little\n");
    }
    else if constexpr(::std::endian::big == ::std::endian::native)
    {
        print("native endianness: big\n");
    }
    else
    {
        print("native endianness: mixed or unknown\n");
    }
}

Only the matching branch is kept in the final program.

Key takeaways