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.
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?
Reveal 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.
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
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.
- 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
- 1Write the constexpr keyword first, before the return type
- 2Write the return type, here double
- 3Write the function name, here square
- 4Write the parameter list in parentheses, here double x
- 5Write 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.
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.
- square is declared constexpr, so a call can appear in the
static_assertcheck square_plainhas the same body but no constexpr keyword- the commented line shows the call that a plain function cannot make in this constant expression
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
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.
- 1Declare the function
Write square with the constexpr keyword so the compiler may evaluate a call inside a constant expression.
- 2Write the check
static_assert(square(3.0) == 9.0, ...)asks the compiler to confirm the comparison while it translates the program. - 3Compile
The compiler evaluates
square(3.0), gets 9.0, and finds the comparison true, so the check passes. - 4Change 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.
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.
Compilation fails at this static_assert line, because square(3.0) evaluates to 9.0, not 10.0.- the call
square(3.0)is evaluated during compilation - the comparison with 10.0 is false, so the
static_assertfails
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 likedouble square_plain(double x) { return x * x; } used inside a static_assertWhy it failsEligibility comes from the constexpr declaration, not from how simple the body looks. A plain function does not qualify.
Do this insteadAdd 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 likestatic_assert(square(3.0) == 10.0, ...) fails and the learner rewrites the multiplication
Why it failsThe failure is reported at the
static_assertline, where the computed value is compared with the expectation. The expected value was wrong, sincesquare(3.0)is 9.0.Do this insteadRecompute 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 failsThe pattern puts constexpr first, before the return type, followed by the name, the parameter list, and the body.
Do this insteadWrite the declaration in the order constexpr, return type, name, parameter list, body, and check each piece.
Practice: Writing Your Own constexpr Function
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.