Concepts / Constant Expressions in C++

Constant Expressions in C++

When a call to it appears inside a constant expression, which is an expression the compiler itself evaluates, the compiler can compute the function's result during translation.

  • Programming

Why the Compiler Might Run Your Function

Most functions you have written so far run only when the program is running. The compiler translates your source code, and the function's body is executed later, when the program starts doing work. This article introduces a different kind of function, one that the compiler itself can evaluate while it is still translating your program. Functions of this kind are the key to constant expressions in C++.

What do you think happens?

Consider this definition: constexpr double square(double x) { return x * x; }. What does the constexpr keyword change about the function?

  • It makes the function run faster when the program is running

  • It makes the function eligible for the compiler to evaluate inside a constant expression

  • It changes the return type from double to an integer

  • It stops the function from accepting a parameter

Reveal answer
Answer:

It makes the function eligible for the compiler to evaluate inside a constant expression.

The keyword does not change the return type or the parameter list. Its practical effect is on where a call's result may be used: a call to the function can take part in an expression that the compiler itself evaluates.

Tracing a Call Through Translation

An ordinary function has a body that waits for the program to run. When the call happens during execution, the body runs then. A constexpr function gives the compiler a different option. When a call to it appears inside a constant expression, which is an expression the compiler itself evaluates, the compiler can compute the function's result during translation. The program then does not have to produce that value while it runs.

Translationcompiler reads square andthe checkConstant evaluationsquare(3.0) computed as 9.0Compiled programthe result is already knownProgram executionno square call needed forthis checkconstant expression foundvalue determinedprogram starts
At which phase does the body of square run when the call appears inside a constant expression?

Read the timeline from left to right. The body of square is evaluated during translation, in the phase where the constant expression is checked. Only then does the program start, and by that point the value needed by the constant expression is already decided.

What the constexpr Keyword Declares

Definition

A constexpr function is a function declared with the constexpr keyword so that the compiler can evaluate it at compile time. A constant expression is an expression evaluated by the compiler at compile time, and constexpr functions are the kind of function that can appear inside one.

The useful way to think about this 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 by itself describes the whole idea. The keyword is a request that the function be eligible for compile-time evaluation, and its practical effect is on where the function's result may be used.

The Declaration Pattern Step by Step

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 a declaration by naming each piece in order.

cpp
Notes on the code
  • constexpr marks the function as eligible for compile-time evaluation
  • double is the return type
  • square is the function name
  • double x is the parameter list
  • the braces contain the body that returns x times x
  1. 1
    Write the constexpr keyword first, before the return type
  2. 2
    Write the return type, here double
  3. 3
    Write the function name, here square
  4. 4
    Write the parameter list in parentheses, here double x
  5. 5
    Write the body in braces, returning the computed value

The pattern is a template to check against, not a separate statement you write on its own. When you read a declaration, identify each piece in order. If the keyword is missing, the function is not declared constexpr, and it cannot be used in a constant expression.

The Eligibility Check Before a Call

When the compiler meets a call to a function inside a constant expression, it looks at the declaration of that function. 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 computes the same value does not qualify just because its body is simple.

Call inside aconstantexpression?Declared constexpr?checked on the declarationCompiler evaluatesthe callcall is eligibleNot usable in aconstant expressioneligibility failsyesyesno
The check the compiler makes on a function declaration when a call appears inside a constant expression.

Follow the path in the diagram. The question that matters is answered at the declaration, not at the call site. However the call is written, a function without the constexpr keyword does not reach the branch where the compiler can evaluate it. This is why adding the keyword to a declaration changes what its calls can be used for, while leaving the body untouched.

Constexpr Versus Ordinary 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.

cpp
Notes on the code
  • square is declared constexpr, so a call can appear in the static_assert check
  • square_plain has the same body but no constexpr keyword
  • the commented line shows the call that a plain function cannot make in this constant expression
Declarationdouble square_plain(doublex)Declarationconstexpr doublesquare(double x)Call in a constantexpressionnot acceptedCall in a constantexpressionacceptedBody evaluatedwhen the program runsBody evaluatedby the compiler in aconstant expression
Which uses of a call are accepted, and where the function body is evaluated, for each kind of function.

The two functions above differ only in the keyword. Both bodies compute x times x. The constexpr version can appear in the static_assert check because the compiler evaluates that check. The plain version cannot, because nothing about its declaration makes it eligible for constant evaluation.

Worked Example: Checking square at Compile Time

Worked example

Checking square at compile time

Confirm during compilation that square(3.0) equals 9.0, and see what happens when the expected value is wrong.

  1. 1
    Declare the function

    Write square with the constexpr keyword so the compiler may evaluate a call inside a constant expression.

  2. 2
    Write the check

    static_assert(square(3.0) == 9.0, ...) asks the compiler to confirm the comparison while it translates the program.

  3. 3
    Compile

    The compiler evaluates square(3.0), gets 9.0, and finds the comparison true, so the check passes.

  4. 4
    Change the expectation

    If the check instead said square(3.0) == 10.0, the comparison would be false. Square(3.0) is still 9.0, so the failure is in the expected value, not in the arithmetic of square.

Result

The check passes when it expects 9.0. When it expects 10.0, the failure is reported at the static_assert line, because that is where the compiler compares the computed value with the expectation.

cpp
Output
Compilation fails at this static_assert line, because square(3.0) evaluates to 9.0, not 10.0.
Notes on the code
  • the call square(3.0) is evaluated during compilation
  • the comparison with 10.0 is false, so the static_assert fails

Mistakes Beginners Make with Compile-Time Checks

  • Common mistake

    Expecting a plain function with a simple body to work in a constant expression

    What it looks like
    double square_plain(double x) { return x * x; } used inside a static_assert
    Why it fails

    Eligibility comes from the constexpr declaration, not from how simple the body looks. A plain function does not qualify.

    Do this instead

    Add the constexpr keyword to the declaration, placed before the return type.

  • Common mistake

    Searching the body of square for a bug when a static_assert fails

    What it looks like

    static_assert(square(3.0) == 10.0, ...) fails and the learner rewrites the multiplication

    Why it fails

    The failure is reported at the static_assert line, where the computed value is compared with the expectation. The expected value was wrong, since square(3.0) is 9.0.

    Do this instead

    Recompute the expected value by hand and correct the comparison at the static_assert line.

  • Common mistake

    Placing the keyword in a position the pattern does not describe

    Why it fails

    The pattern puts constexpr first, before the return type, followed by the name, the parameter list, and the body.

    Do this instead

    Write the declaration in the order constexpr, return type, name, parameter list, body, and check each piece.

Practice: Writing Your Own constexpr Function

EASY

Write a constexpr function named cube that takes one double and returns the value multiplied by itself three times. Then write a static_assert that checks cube(2.0) equals 8.0. Before compiling, state which phase evaluates the call to cube.

Hints
  • Follow the pattern: constexpr, then double, then cube, then the parameter list, then the braces.
  • The body can return x * x * x.
  • The call to cube sits inside the static_assert, so the compiler evaluates it during translation.

Key Takeaways

  • A constexpr function is declared with the constexpr keyword so that the compiler can evaluate it at compile time.
  • A constant expression is an expression the compiler evaluates during translation, and constexpr functions are the kind of function that can appear inside one.
  • The declaration pattern is constexpr, then the return type, the function name, the parameter list, and the body in braces.
  • A function must be declared constexpr before its calls can take part in a constant expression; a plain function with a simple body does not qualify.
  • When a compile-time check fails, the error is reported at the line where the comparison is made, so the expected value should be checked first.

Related concepts

More concepts starting with C