Constexpr
By the end of this lesson you'll be able to push real work out of run time and into the build itself — writing constexpr variables and functions, telling const , constexpr , and consteval apart, branching at compile time with if constexpr , and proving facts with static_assert so mistakes fail the build, not your users.
Part of the free C++ course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn
💡 Real-World Analogy
Imagine a chef prepping for a dinner service. Some work can be done ahead of time — chopping vegetables, making stock, baking bread. That's compile-time work: done once, before any customer arrives, so service is fast. Other work has to wait for the order — you can't sear a steak until the customer says "medium-rare". That's run-time work. constexpr is you telling the compiler "this can be prepped ahead" — if all the ingredients (the inputs) are known in advance, the answer is ready and waiting before the program ever runs. The result is baked into the binary at zero runtime cost.
1. constexpr Variables & Functions
Put constexpr in front of a function and you're telling the compiler "this can be evaluated while compiling". Put it in front of a variable and you're telling it "evaluate this now , at compile time". If the inputs are known during the build, the work happens then and the answer is stored as a plain constant — nothing is computed when the program runs. Read this worked example carefully, run it, then you'll write your own.
Your turn. The program below is almost complete — fill in the three blanks marked ___ using the // 👉 hints, then run it. You're adding the constexpr keyword in the two right places and a static_assert to check the result at build time.
2. const vs constexpr vs consteval
These three keywords are easy to mix up, but each answers a different question. const asks "can this change?" — no, but it might still be decided at run time. constexpr asks "is this known at compile time?" — yes, and that also makes it const . consteval (C++20) is the strict version: "this must run at compile time" , with no runtime fallback at all. The worked example shows all three side by side.
🔎 Deep Dive: the same function, two modes
A constexpr function is not guaranteed to run at compile time — it's allowed to. The deciding factor is the inputs . Feed it compile-time constants and use the result where a constant is required, and it runs during the build. Feed it a runtime variable and the very same function runs at run time instead.
This is the superpower: one implementation serves both worlds. Want to forbid the runtime path entirely (so a runtime call is a compile error)? Use consteval instead of constexpr .
3. Branching at Compile Time with if constexpr
if constexpr (C++17) chooses a branch while compiling , based on a constant or a type trait like is_integral_v<T> . The branch that isn't taken is thrown away before it's even type-checked — so one template can do genuinely different things for different types. A normal if can't do that, because both branches must compile for every type. This is the standard way to write code that adapts to T .
Now you try. The template below should double whole numbers but halve decimals. Fill in the one blank so the branch is chosen at compile time, then run it:
Pro Tips
- 💡 Prove it ran at compile time with static_assert or by using the value as an array size — both only accept genuine compile-time constants.
- 💡 Reach for consteval (C++20) when a runtime call would be a bug — it turns that mistake into a compile error.
- 💡 Lookup tables are perfect candidates: trig tables, CRC tables, and hash seeds can be generated once at compile time and shipped as constants.
- 💡 Keep constexpr functions simple. They must be pure (no I/O, no surprises) to be usable in a constant context.
Common Errors (and the fix)
- "call to non- constexpr function" / "is not a constant expression": you called something that isn't constexpr (or does I/O like cout ) inside a constexpr context. Everything a constexpr function touches must itself be usable at compile time — make the helper constexpr too, and keep it pure.
- "the value of 'n' is not usable in a constant expression": you tried to initialise a constexpr variable from a runtime value. constexpr int x = userInput; can't work because userInput isn't known at build time. Use plain const if the value is only known at run time.
- "call to consteval function ... is not a constant expression": you called a consteval function with a runtime argument. consteval has no runtime mode — pass a literal/ constexpr value, or switch the function to constexpr if you genuinely need the runtime path.
- Array size won't compile: int a[size]; where size isn't a compile-time constant. Mark it constexpr int size = ...; so the size is fixed during the build.
- Using if where you needed if constexpr : the untaken branch still has to compile and fails for some T . Change it to if constexpr so the dead branch is discarded.
📋 Quick Reference
Keyword
Means
Since
const
Can't change after it's set (may be a runtime value)
C++98
constexpr
Known at compile time if inputs are; also const
C++11
if constexpr
Pick a branch at compile time; drop the other
C++17
consteval
MUST run at compile time (no runtime fallback)
C++20
constinit
MUST be initialised at compile time
static_assert
Check a condition while compiling; fail the build if false
Frequently Asked Questions
Mini-Challenge: Compile-Time Fibonacci
No blanks this time — just a brief and a near-empty canvas (with an outline to keep you on track). Write the constexpr function yourself, force the result at compile time, and use static_assert to prove it. Run it and check your output against the example in the comments.
🎉 Lesson Complete
- ✅ constexpr on a function means it can run at compile time; on a variable it means compute it now
- ✅ const = won't change; constexpr = known at compile time; consteval = must be compile time
- ✅ The same constexpr function runs at compile time or run time depending on its inputs
- ✅ if constexpr picks a branch while compiling and discards the other — perfect for type-based logic
- ✅ static_assert checks facts during the build, so wrong assumptions fail the compile, not the user
- ✅ Compile-time work means faster programs (zero runtime cost) and safer ones (errors caught early)
- ✅ Next lesson: Modern C++17 & C++20 Features — structured bindings, concepts, ranges, and more
Practice quiz
What does 'constexpr' on a function mean?
- It must run at compile time
- It runs only at run time
- It CAN run at compile time when its inputs are compile-time constants
- It disables optimization
Answer: It CAN run at compile time when its inputs are compile-time constants. constexpr means 'allowed at compile time', not 'always' — the inputs decide which mode is used.
What does 'constexpr' on a variable mean?
- Compute it now, at compile time
- Compute it lazily at run time
- It can change later
- It is thread-local
Answer: Compute it now, at compile time. A constexpr variable forces compile-time evaluation; the result is baked into the binary as a constant.
When does a constexpr function actually run at compile time?
- Always
- Only inside main()
- Never with recursion
- Only when all arguments are compile-time constants and the result is used in a constant context
Answer: Only when all arguments are compile-time constants and the result is used in a constant context. Give it constant inputs used where a constant is required and it runs during the build; give it a runtime value and it runs at run time.
What does consteval (C++20) add over constexpr?
- It is just a synonym
- It removes the runtime fallback — every call must be evaluated at compile time
- It allows I/O
- It makes the function virtual
Answer: It removes the runtime fallback — every call must be evaluated at compile time. A consteval function must run at compile time; calling it with a runtime value is a compile error.
Which keyword can hold a value decided at RUN time yet never change afterward?
- const
- constexpr
- consteval
- constinit
Answer: const. const says 'won't change' but says nothing about WHEN it's known — it can be set from a runtime value.
Why use 'if constexpr' instead of a normal 'if' in a template?
- It runs faster at runtime
- It allows more branches
- The untaken branch is discarded before it's even type-checked, so one template can adapt to different types
- It works only with integers
Answer: The untaken branch is discarded before it's even type-checked, so one template can adapt to different types. A normal if forces both branches to compile for every type; if constexpr drops the dead branch, so type-specific code compiles.
What does static_assert do?
- Checks a condition at run time
- Checks a constexpr condition while compiling and fails the build if it's false
- Logs a warning
- Allocates static storage
Answer: Checks a constexpr condition while compiling and fails the build if it's false. static_assert verifies a compile-time condition; a false condition stops the build with your message.
In the lesson, the template branches: integers get doubled, everything else halved. What does transform(9.0) yield?
- 18
- 9
- 4
- 4.5
Answer: 4.5. 9.0 is a double, so the else branch (value / 2) runs, giving 4.5.
What is one reason to move work to compile time?
- Larger binaries always
- Zero runtime cost — the value is just a constant in the binary — plus earlier error detection
- It avoids needing a compiler
- It enables dynamic typing
Answer: Zero runtime cost — the value is just a constant in the binary — plus earlier error detection. Compile-time results cost nothing at run time, and static_assert catches wrong assumptions during the build.
Calling a consteval function with a runtime variable argument results in:
- A runtime call
- A silent default value
- A compile error
- An exception
Answer: A compile error. consteval has no runtime mode, so a runtime argument is a compile error — pass a literal/constexpr value instead.
Continue this course
- Previous: RAII Architecture
- Next: C++17 & C++20 Modern Features