Concepts / Understanding Constants in C++

Understanding Constants in C++

The word constant is meant literally here: the value is fixed while the program is still being translated.

  • Programming

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?

  • Yes, the compiler evaluates the call and checks the result

  • No, the compiler rejects the line because the function is not eligible for a constant expression

Reveal answer
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.

Source codesquare(3.0) is writtenCompiler translatesreads the constexpr callCompile-timeevaluationcompiler computes the valueConstant valueresult used in thetranslated programProgram runslater, at program startconstant expression
What steps does the compiler take from seeing a constexpr call to producing a constant value, and where does that happen relative to running the program?

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

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 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.

Key point

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.

Function call in aconstant expressionCompiler evaluatesthe callvalue used at compile timeDeclared constexpr?Not usable inconstant expressioncompile erroryesno
When the compiler reaches a call inside a constant expression, how does it decide whether that call is allowed there?

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.

cpp
Notes on the code
  • 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.

constexpreligibility keyword, firstdoublereturn typesquarefunction name(double x)parameter list{ return x * x; }body in braces
Which parts of a constexpr function declaration are required, and where does the constexpr keyword go relative to the return type?

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.

cpp
Output
The program compiles. The check passes at compile time, and nothing is printed.
Notes on the code
  • 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
Worked example

Following the Compiler Through One Check

Show what the compiler does with static_assert(square(3.0) == 9.0, ...) when square is declared constexpr.

  1. 1
    Find the constant expression

    The static_assert condition must be decided during compilation, so the expression square(3.0) == 9.0 is a constant expression.

  2. 2
    Check eligibility of the call

    The call square(3.0) appears inside that expression. square was declared with the constexpr keyword, so it is eligible.

  3. 3
    Evaluate the body

    The compiler runs the body of square with x set to 3.0, which computes 3.0 times 3.0.

  4. 4
    Compare the result

    The computed value 9.0 is compared with 9.0, and the condition is true.

  5. 5
    Accept the program

    Because the condition holds, the static_assert produces no error and the program continues to be translated.

Result

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.

squaremay appear in a constantexpressioncube_plainnot eligible for a constantexpressionstatic_assert(square(3.0)== 9.0)compiler evaluates itstatic_assert(cube_plain(3.0)== 27.0)rejected by the compiler
Which call sites can the compiler evaluate during compilation, and which must wait until the program runs?
Featureconstexpr functionOrdinary function
Declaration keywordconstexpr before the return typeNo constexpr keyword
May appear in a constant expressionYes, once declared constexprNo, even if the body is simple
Evaluated by the compilerEligible for compile-time evaluationNot 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.

cpp
Output
Compilation fails at the static_assert line, reporting the message that the expected value does not match.
Notes on the code
  • 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_assert line

Mistakes with constexpr Declarations

  • Common mistake

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

    What it looks like
    static_assert(cube_plain(3.0) == 27.0, "...") where cube_plain has no constexpr keyword
    Why it fails

    The source states that a plain function does not qualify just because its body is simple. Eligibility comes from the declaration.

    Do this instead

    Declare 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 like
    double constexpr square(double x)
    Why it fails

    The general pattern puts the keyword first, before the return type. Each piece of the declaration has a fixed place.

    Do this instead

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

  • Common mistake

    Expecting the keyword to change how the body is written

    Why it fails

    The keyword marks eligibility. The body still computes a value with ordinary statements such as return x * x.

    Do this instead

    Keep 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

EASY

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.

Related concepts

More concepts starting with U