Question
constexpr vs const in C++: What’s the Difference and When to Use Each
Question
What is the difference between constexpr and const in C++?
When can only one of them be used? When can both be used, and how should you decide which one to choose?
Short Answer
By the end of this page, you will understand that const means a value cannot be modified after initialization, while constexpr means a value or function can be evaluated at compile time when possible. You will learn when they overlap, when they differ, and how to choose the right one in practical C++ code.
Concept
const and constexpr are related, but they are not the same thing.
const
const means read-only after initialization. Once a const variable has been given a value, your program cannot change it.
const int x = 10;
// x = 20; // error
This does not automatically mean the value is known at compile time.
int n = getValue();
const int x = n; // valid if n is available at runtime
Here, x is still read-only, but its value depends on runtime data.
constexpr
constexpr means the value is intended to be a constant expression, which means the compiler can compute it at compile time.
constexpr int x = 10;
A constexpr variable must be initialized with a compile-time constant expression.
int n = 5;
constexpr int x = n; // error: n is not a constant expression
Key idea
const= cannot be changedconstexpr= known at compile time
A constexpr object is also effectively const, because if something is known at compile time and fixed, you cannot later modify it.
Why this matters
The distinction matters because some language features require values to be known at compile time, such as:
- array sizes in some contexts
- template arguments
caselabels inswitch- compile-time calculations
- constant initialization for performance or correctness
So if you only need a read-only variable, const may be enough. If the compiler must know the value during compilation, you need constexpr.
Mental Model
Think of const and constexpr like labels on a box:
constmeans "this box is sealed after you fill it".constexprmeans "this box must be filled before the factory ships it".
With const, you can fill the box later at runtime, then seal it.
With constexpr, the box must already be filled during compilation.
So:
constfocuses on immutabilityconstexprfocuses on compile-time availability
Syntax and Examples
Basic syntax
const int a = 10;
constexpr int b = 10;
Both are read-only, but b is also guaranteed to be a constant expression.
Example 1: const with a runtime value
#include <iostream>
int getAge() {
return 25;
}
int main() {
const int age = getAge();
std::cout << age << '\n';
}
age cannot be changed after initialization, but it is not necessarily known at compile time because it comes from a function call that runs at runtime.
Example 2: constexpr with a compile-time value
constexpr int daysInWeek = 7;
constexpr int hoursInDay = 24;
constexpr int hoursInWeek = daysInWeek * hoursInDay;
The compiler can calculate hoursInWeek before the program runs.
Example 3: constexpr function
constexpr int square(int x) {
return x * x;
}
constexpr int result = square(5);
Because square(5) can be evaluated at compile time, result is valid as a constexpr.
Example 4: When both seem to work
const int a = 100;
constexpr int b = 100;
Both are fine here. The choice depends on intent:
- use
constif you only mean read-only - use
constexprif you want compile-time constant semantics
Example 5: Compile-time required context
constexpr int size = 5;
int arr[size];
This works because size is a constant expression.
A plain const variable may or may not work depending on context and standard rules, but constexpr clearly expresses compile-time intent.
Step by Step Execution
Consider this example:
#include <iostream>
constexpr int add(int a, int b) {
return a + b;
}
int main() {
const int x = 10;
constexpr int y = 20;
constexpr int z = add(2, 3);
std::cout << x << ' ' << y << ' ' << z << '\n';
}
Step by step
1. The compiler sees constexpr int add(int a, int b)
This tells the compiler that add can be evaluated at compile time if its arguments are constant expressions.
2. const int x = 10;
xis read-only- it cannot be reassigned
- this line does not primarily promise compile-time use; it promises immutability
3. constexpr int y = 20;
yis read-onlyyis known at compile time- it can be used where a constant expression is required
4. constexpr int z = add(2, 3);
2and3are compile-time constantsadd(2, 3)is computed during compilationzbecomes5
5. std::cout << x << ' ' << y << ' ' << z
At runtime, the program prints the values. But some of the work, especially computing z, was already done at compile time.
Important nuance
A constexpr function can also run at runtime if you pass runtime values:
constexpr int square(int x) {
return x * x;
}
int n;
std::cin >> n;
int result = square(n); // valid, but evaluated at runtime
So constexpr on a function means can be compile-time evaluated, not must always be.
Real World Use Cases
When const is useful
Read-only configuration loaded at runtime
const std::string filePath = readConfigFilePath();
The path is not known during compilation, but you still want to prevent accidental modification.
Function parameters that should not change
void printMessage(const std::string& message);
This tells readers and the compiler that the function will not modify message.
Protecting member functions and objects
class User {
public:
std::string getName() const;
};
Here, const is part of API design and correctness.
When constexpr is useful
Compile-time constants
constexpr double pi = 3.141592653589793;
Useful for math, geometry, and other fixed values.
Array sizes and fixed limits
constexpr int maxUsers = 100;
Compile-time helper functions
constexpr int bufferSize(int kb) {
return kb * 1024;
}
Template and metaprogramming contexts
template <int N>
struct Buffer {};
constexpr int size = 8;
Buffer<size> b;
Template arguments like size must be compile-time constants.
When both ideas matter
Sometimes you want a value that is both:
- read-only
- guaranteed compile-time
That is exactly what constexpr gives you for variables.
Real Codebase Usage
In real projects, developers usually choose based on intent.
Common pattern: use const for runtime immutability
const auto userId = request.getUserId();
const auto timeout = config.readTimeout();
These values come from runtime data, so constexpr is not possible.
Common pattern: use constexpr for true constants
constexpr int maxRetries = 3;
constexpr char apiVersion[] = "v1";
These are fixed program constants and may be used in compile-time contexts.
Guard clauses and validation
void connect(int port) {
constexpr int minPort = 1;
constexpr int maxPort = 65535;
if (port < minPort || port > maxPort) {
throw std::out_of_range("invalid port");
}
}
This makes limits explicit and safe.
Class constants
class Math {
public:
static constexpr double pi = 3.141592653589793;
};
Real codebases often use static constexpr for values shared by all instances.
Compile-time helper utilities
constexpr bool isPowerOfTwo(unsigned int x) {
return x > 0 && (x & (x - 1)) == 0;
}
These functions are useful in libraries where values may be known at compile time in some places and only at runtime in others.
Rule of thumb in production code
- If the value may depend on runtime input, use
const. - If the value is truly fixed and compile-time meaningful, use
constexpr. - If both are possible, prefer
constexprwhen compile-time semantics are useful and intentional.
Common Mistakes
1. Thinking const always means compile-time constant
Broken assumption:
int getValue();
const int x = getValue();
x is read-only, but its value is not known at compile time.
How to avoid it
Ask:
- Is this just immutable?
- Or does the compiler need to know it during compilation?
2. Using constexpr with a runtime value
Broken code:
int n = 10;
constexpr int x = n; // error
Why it fails
n is not a constant expression.
Fix
constexpr int n = 10;
constexpr int x = n;
3. Assuming constexpr functions only run at compile time
Broken assumption:
constexpr int doubleValue(int x) {
return x * 2;
}
int n;
std::cin >> n;
int result = doubleValue(n);
This is valid. The function runs at runtime here because n is only known at runtime.
How to avoid it
Remember: constexpr function means eligible for compile-time evaluation.
4. Using const when compile-time use is required
Broken code:
const int size = getSize();
// int arr[size]; // may fail or be non-portable depending on context
Fix
Use constexpr if the size must be known at compile time.
constexpr int size = 10;
int arr[size];
5. Forgetting that const has many uses beyond variables
Beginners sometimes think this topic is only about constants, but const also applies to:
- references
- pointers
- member functions
- objects
Example:
void show(const std::string& name);
This is about preventing modification, not compile-time evaluation.
Comparisons
const vs constexpr
| Feature | const | constexpr |
|---|---|---|
| Prevents modification after initialization | Yes | Yes |
| Requires compile-time value | No | Yes |
| Can use runtime initialization | Yes | No |
| Can be used where constant expression is required | Not guaranteed | Yes |
| Best meaning | Read-only | Compile-time constant |
Variables: when to choose
| Situation | Preferred choice | Why |
|---|---|---|
| Value comes from user input or file config | const | It is runtime data |
| Fixed mathematical or program constant | constexpr | Known during compilation |
| You only want to avoid reassignment | const | Simpler and accurate |
| You need compile-time use in templates or constant expressions | constexpr | Required by the language |
const vs #define
| Feature | const / constexpr | #define |
|---|---|---|
| Type-safe | Yes | No |
| Scoped | Yes | No |
| Checked by compiler | Yes | No textual macro only |
| Preferred in modern C++ | Yes | Usually yes |
constexpr vs consteval
| Feature | constexpr | consteval |
|---|---|---|
| Can run at compile time | Yes | Yes |
| Must run at compile time | No | Yes |
| Beginner use case | Common | Less common |
consteval is stricter, but for most constant values and functions, constexpr is the practical tool.
Cheat Sheet
Quick rules
const= read-only after initializationconstexpr= compile-time constant- Every
constexprvariable is effectivelyconst - Not every
constvariable isconstexpr
Syntax
const int a = 10;
constexpr int b = 10;
constexpr int square(int x) {
return x * x;
}
Use const when
- the value is decided at runtime
- you want to prevent accidental changes
- you are working with parameters, references, pointers, or member functions
Use constexpr when
- the value is known during compilation
- the value may be used in constant-expression contexts
- you want compile-time computation
Common examples
const int port = readPort();
constexpr int maxConnections = 100;
constexpr int area = square(4);
Decision shortcut
- Can the value depend on runtime input?
- Yes ->
const - No -> maybe
constexpr
- Yes ->
- Does the compiler need the value at compile time?
- Yes ->
constexpr
- Yes ->
Important edge case
A constexpr function:
- can run at compile time with constant-expression arguments
- can also run at runtime with normal arguments
FAQ
Is constexpr always better than const?
No. Use constexpr when a compile-time constant is intended. Use const when you only need a read-only value, especially if it comes from runtime data.
Is every constexpr variable also const?
Yes, a constexpr variable is immutable and acts like a constant.
Can a const variable be initialized at runtime?
Yes. That is one of the main differences. const does not require compile-time initialization.
Can a constexpr function be called at runtime?
Yes. If the arguments are not constant expressions, the function can still execute at runtime.
Should I use constexpr for all fixed numeric constants?
Usually yes, if they are truly compile-time constants and that meaning is useful in the code.
Why not just use #define for constants?
Because constexpr and const are type-safe, scoped, and checked by the compiler.
Can I use both const and constexpr together?
For variables, constexpr already implies const-like immutability, so writing both is usually unnecessary.
When does const matter beyond variables?
It is heavily used with references, pointers, member functions, and APIs to express that something should not be modified.
Mini Project
Description
Build a small C++ program that demonstrates the difference between runtime read-only values and compile-time constants. This project is useful because many real programs contain both kinds of data: configuration read at runtime and fixed limits or formulas known at compile time.
Goal
Create a program that uses const for runtime values and constexpr for compile-time values, then prints results showing the difference.
Requirements
[ "Create a constexpr function that performs a simple calculation.", "Declare at least one constexpr variable initialized with a compile-time value.", "Read or simulate a runtime value and store it in a const variable.", "Print both the compile-time and runtime-based results.", "Use clear variable names that show why each keyword was chosen." ]
Keep learning
Related questions
Advantages of Brace Initialization in C++
Learn why C++ brace initialization is often clearer and safer than other object initialization styles, with examples and common pitfalls.
Basic Rules and Idioms for Operator Overloading in C++
Learn the core rules, syntax, and common idioms for operator overloading in C++, including member vs non-member operators.
C++ Aggregates, Trivial Types, Trivially Copyable Types, and PODs Explained
Learn what aggregates, trivial types, trivially copyable types, and PODs mean in C++, how they differ, and why they matter.