Understanding Constants in C++
The word constant is meant literally here: the value is fixed while the program is still being translated.
A Function Call You Can Check Before Running
Most functions you have written so far run only when the program is running. The compiler translates your source code, and the body of the function is executed later, when the program starts doing work. This section introduces a different kind of function, one that the compiler itself can evaluate while it is still translating your program. The keyword that makes this possible is constexpr.
What do you think happens?
Suppose a plain function named cube_plain exists, with a normal declaration and no special keyword. You write a static_assert that calls it. Will the compiler accept that line?
Reveal answer
No, the compiler rejects the line because the function is not eligible for a constant expression
The source states that a function must be defined constexpr before it can be used in a constant expression. A plain function that computes the right value still does not qualify just because its body is simple. Keep this answer in mind as you read the rest of the article.
Where the Compiler Does Its Work
A program passes through two stages that matter here. The compiler translates the source code you wrote. Later, the translated program runs. For an ordinary function, the work of its body belongs to the second stage: the compiler records what the function does, and the body executes when the program reaches the call while running.
A constexpr function changes this picture. It 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 result may be used: a call to the function can take part in a constant expression, which is an expression the compiler itself evaluates.
The diagram shows the path for a constexpr call inside a constant expression. The step that separates it from an ordinary call is the compile-time evaluation step, which happens before the program runs. The word constant is meant literally here: the value is fixed while the program is still being translated.
Eligibility as the Deciding Factor
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 relationship has two parts. 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. A declaration with the keyword without any constant expression that uses it does nothing visible, and a constant expression that calls a function without the keyword has no permission to use it.
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.
Reading a constexpr Declaration
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. The pattern is a template to check against, not a separate statement you write on its own.
- constexpr marks the function as eligible for compile-time evaluation
- double before square is the return type
- square is the function name
- double x is the parameter list
- the braces hold the body, which returns x times x
Read the 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. Notice that the keyword is the first word of the declaration, not something placed after the parameter list or inside the body.
Tracing a square Call from Declaration to Check
Now follow one call through the whole process. The declaration above defines square. Next, a static_assert line uses square inside a constant expression. A static_assert checks a condition when the program is compiled. The compiler must evaluate square(3.0) to decide whether the condition holds, which is only possible because square is declared constexpr.
The program compiles. The check passes at compile time, and nothing is printed.- square is declared constexpr, so its call is eligible in a constant expression
- the compiler evaluates
square(3.0)while translating the program - the comparison with 9.0 is decided before the program runs
Following the Compiler Through One Check
Show what the compiler does with static_assert(square(3.0) == 9.0, ...) when square is declared constexpr.
- 1Find the constant expression
The
static_assertcondition must be decided during compilation, so the expressionsquare(3.0)== 9.0 is a constant expression. - 2Check eligibility of the call
The call
square(3.0)appears inside that expression. square was declared with the constexpr keyword, so it is eligible. - 3Evaluate the body
The compiler runs the body of square with x set to 3.0, which computes 3.0 times 3.0.
- 4Compare the result
The computed value 9.0 is compared with 9.0, and the condition is true.
- 5Accept the program
Because the condition holds, the
static_assertproduces no error and the program continues to be translated.
The condition is decided during compilation, and the program compiles because square(3.0) equals 9.0.
Comparing Plain 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.
| Feature | constexpr function | Ordinary function |
|---|---|---|
| Declaration keyword | constexpr before the return type | No constexpr keyword |
| May appear in a constant expression | Yes, once declared constexpr | No, even if the body is simple |
| Evaluated by the compiler | Eligible for compile-time evaluation | Not eligible for compile-time evaluation through a constant expression |
Plain and constexpr functions differ in eligibility, not in how they are written
Reading a Failed Compile-Time Check
When a compile-time check fails, the error location tells you where the expression was evaluated, which is the first clue to a wrong result. Suppose you expected square(3.0) to equal 10.0 and wrote the check accordingly. The arithmetic inside square is correct, so the failure is not inside its body. It is at the static_assert line, because that line is where the compiler compares the computed value with your expectation.
Compilation fails at the static_assert line, reporting the message that the expected value does not match.- square itself is correct, since 3.0 times 3.0 is 9.0
- the mismatch is detected where the comparison is made, on the
static_assertline
Mistakes with constexpr Declarations
Common mistake Assuming a simple body makes a function usable in a constant expression
What it looks likestatic_assert(cube_plain(3.0) == 27.0, "...") where cube_plain has no constexpr keywordWhy it failsThe source states that a plain function does not qualify just because its body is simple. Eligibility comes from the declaration.
Do this insteadDeclare the function constexpr, following the pattern constexpr, return type, name, parameter list, body.
Common mistake Placing constexpr somewhere other than the start of the declaration
What it looks likedouble constexpr square(double x)Why it failsThe general pattern puts the keyword first, before the return type. Each piece of the declaration has a fixed place.
Do this insteadWrite constexpr double square(double x) with the keyword first.
Common mistake Expecting the keyword to change how the body is written
Why it failsThe keyword marks eligibility. The body still computes a value with ordinary statements such as return x * x.
Do this insteadKeep the body as a normal function body and add the keyword to the declaration.
Common mistake Blaming the arithmetic when a static_assert fails, instead of checking the expected value on the static_assert line
Practice: Declare and Check
Write a constexpr function named triple that takes one double and returns three times its value. Then write a static_assert that checks triple(2.0) equals 6.0. Explain in one sentence why the keyword must appear on triple's declaration for the check to be allowed.
Hints
- Start from the pattern: constexpr, return type, name, parameter list, body.
- The keyword goes before the return type, as in the square example.
- The check passes only if the call is eligible for a constant expression, which is determined by the declaration.
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 evaluated by the compiler, and a call to a constexpr function may take part in one.
- A function must be declared constexpr before it can be used in a constant expression; a simple body alone does not qualify it.
- The declaration follows the pattern constexpr, return type, function name, parameter list, and body in braces.
- The keyword changes eligibility, not the way the body is written, so a failed compile-time check points to the expression that was evaluated.