Concepts / Compile-Time Evaluation with static_assert

Compile-Time Evaluation with static_assert

A constexpr function is declared with the constexpr keyword so that the compiler can evaluate it at compile time.

  • Programming

A Function the Compiler Can Run

Most of the functions you have written so far run only when the program is running. The compiler translates your source code into a program, and each function body is executed later, when the program starts doing its work. This topic introduces a different kind of function: one that the compiler itself can evaluate while it is still translating your program.

The tool that makes this possible is a single keyword, constexpr, placed on the function declaration. Along with static_assert, which asks the compiler to check a condition during translation, it lets you have the compiler verify a value before the program ever runs. The rest of this article follows what the compiler does at each step, so you can see exactly where the keyword changes the outcome.

What the constexpr Keyword Changes

cpp

Read this declaration as a complete definition. It creates a function named square that takes one double and returns a double. The only new word is constexpr, placed before the return type. Before reading on, consider what that one keyword might change, because the answer is the central idea of this article.

What do you think happens?

Given the same body, does adding constexpr change whether a call to square can appear inside a static_assert check?

  • No, the body is identical, so the call behaves the same

  • Yes, constexpr makes the function eligible for constant expressions

Reveal answer
Answer:

Yes, constexpr makes the function eligible for constant expressions, so a call to it can be evaluated by the compiler inside a static_assert check.

The keyword does not change how the arithmetic works. It is a request that the function be eligible for compile-time evaluation, and that eligibility determines where a call may be used.

A constexpr function is a function that can be evaluated by the compiler at compile time. The keyword is a request that the function be eligible for that kind of evaluation. Its practical effect is on where the function's result may be used: a call to the function can take part in a constant expression, which is an expression the compiler itself will evaluate.

Definition

A constant expression is an expression that is evaluated by the compiler at compile time, rather than being left to run when the program executes. constexpr functions are the kind of function that can be used inside these expressions.

The Declaration Pattern

A constexpr function follows a general pattern. The keyword comes first, then the return type, the function name, the parameter list, and finally the body in braces. Each piece has a job, and you can check any declaration by naming the pieces in order.

constexprmarks the function aseligibledoublereturn typesquarefunction name(double x)parameter list{ return x * x; }body in bracesthenthenthenthen
Which part of the square declaration supplies eligibility, the return type, the name, the parameters, and the body?

Applying the pattern to square: constexpr marks eligibility, double is the return type, square is the name, double x is the parameter list, and the braces contain the body that returns x times x. The pattern is a template to check against, not a separate statement you write on its own.

Tracing a static_assert Check

The useful way to think about a constexpr function is as a two-part relationship. The constexpr keyword sits on the function declaration and says the function is eligible. The constant expression is the place where that eligibility is used. Neither part alone describes the whole idea. The following code puts both parts together.

cpp
Notes on the code
  • constexpr makes square eligible for constant expressions
  • static_assert asks the compiler to check the condition during translation
  • square(3.0) is part of the condition, so the compiler must be able to evaluate it
Worked example

Tracing a Failing Compile-Time Check

Suppose square is declared constexpr and the check is written as static_assert(square(3.0) == 10.0). Does the check pass?

  1. 1
    Step 1: Find the call

    The compiler reaches the call square(3.0) inside the static_assert condition.

  2. 2
    Step 2: Confirm eligibility

    Because square is declared constexpr, the call is eligible to be evaluated by the compiler.

  3. 3
    Step 3: Compute the value

    The body returns x times x, so square(3.0) evaluates to 9.0.

  4. 4
    Step 4: Compare with the expectation

    The condition 9.0 == 10.0 is false.

  5. 5
    Step 5: Report the result

    A false static_assert condition is a compile error, reported at the static_assert line.

Result

The check fails at compile time. The failure is not inside the arithmetic of square; it is at the static_assert line, because that line is where the computed value is compared with the expectation.

Why constexpr Is Required

The rule is strict in one direction. For a function to be usable in a constant expression, it must be defined constexpr. Declaring a function constexpr is what makes it eligible. A plain function that happens to compute the same value does not qualify just because its body is simple.

Call insidestatic_assertDeclared constexpr?Evaluate call atcompile timeCompare result withexpectationCheck passesNot eligible forconstant expressionCompile error atstatic_assertyesnotruefalsecannot evaluate
What the compiler checks when a call appears in a constant expression, and where each answer leads.

The diagram shows only the declaration check described in the source. A function declared constexpr moves onto the path where its calls can be evaluated by the compiler. Without that declaration, the function does not qualify for constant expressions, however the call is written. The body and the call can look identical; the declaration is what decides.

Comparing Ordinary and constexpr Functions

Placing the two kinds of function side by side makes the difference easier to see. Both are ordinary-looking functions with a body that returns a value. What changes is whether a call to the function may appear in an expression that the compiler must evaluate.

Runs at program runtimeEvaluable by thecompilerNot usable inconstant expressionsUsable in constantexpressions
Where a call to each kind of function may be evaluated.

The two columns are not about how the arithmetic is written. An ordinary function with the same body still runs when the program runs, and a call to it cannot take part in a constant expression. A constexpr function with that body can be evaluated by the compiler, so its call may appear inside static_assert or in another constant expression.

Mistakes With constexpr Declarations

  • Common mistake

    Assuming a simple body makes a function usable in a constant expression

    What it looks like
    double square(double x) { return x * x; } followed by a static_assert that calls square
    Why it fails

    The function is not declared constexpr, so it does not qualify for constant expressions, no matter how simple its body is.

    Do this instead
    Add the constexpr keyword to the declaration: constexpr double square(double x) { return x * x; }
  • Common mistake

    Placing constexpr after the return type

    What it looks like
    double constexpr square(double x)
    Why it fails

    The declaration pattern puts the keyword first, before the return type, name, parameter list, and body.

    Do this instead

    Write constexpr double square(double x) with the keyword in the first position.

  • Common mistake

    Looking for a bug inside the function when a static_assert fails

    What it looks like

    Rechecking the multiplication in square after static_assert(square(3.0) == 10.0) fails

    Why it fails

    The failure is at the static_assert line, where the computed value is compared with the expectation. The expectation is what is wrong here, not the arithmetic.

    Do this instead

    Compare the expected value with the computed value. square(3.0) is 9.0, so the check should use 9.0.

  • Common mistake

    Forgetting that the compiler, not the running program, evaluates a constant expression

Practice Writing a constexpr Function

EASY

Write a constexpr function named cube that takes one double and returns the cube of its input. Then write a static_assert that checks that cube(2.0) equals 8.0. Before you run the check, name each piece of your declaration in order: keyword, return type, name, parameter list, body.

Hints
  • Follow the same pattern as square: constexpr, return type, name, parameter list, body in braces.
  • The body can multiply the input by itself twice.
  • Your static_assert condition should compare cube(2.0) with 8.0 using ==.

Key Takeaways

  1. 1
    A constexpr function is a function the compiler can evaluate at compile time, and the keyword makes it eligible for constant expressions.
  2. 2
    The declaration pattern puts constexpr first, then the return type, function name, parameter list, and body in braces.
  3. 3
    For a function to be usable in a constant expression, it must be declared constexpr; a simple body alone does not qualify it.
  4. 4
    A failed static_assert is reported at the static_assert line, where the computed value is compared with the expected value.

At a glance

  • A constexpr function is a function the compiler can evaluate at compile time, and the keyword makes it eligible for constant expressions.
  • The declaration pattern is constexpr, then return type, function name, parameter list, and body in braces.
  • A function must be declared constexpr before its call can appear in a constant expression, regardless of how simple its body is.
  • An ordinary function and a constexpr function can have the same body, but only the constexpr one can be evaluated inside a constant expression such as static_assert.

Related concepts

More concepts starting with C