C++ Constants
Understanding Constants
Constants are objects whose values cannot be changed after initialization. In C++, `const` makes an object read-only. This improves safety and clarity by preventing accidental modification.
`const` does not necessarily mean compile-time: a `const` object can be initialized at runtime. Use `constexpr` (and, for initialization-only guarantees of static storage, `constinit`) for compile-time contexts.
Declaring Constants
Use the `const` keyword for read-only objects. Use `constexpr` when you need a compile-time constant expression (e.g., for non-type template parameters, `std::array` sizes, `case` labels).
#include <iostream>
#include <string>
using namespace std;
int main() {
const double PI = 3.141592653589793;
const int MAX_USERS = 100;
const string GREETING = "Hello";
constexpr int DAYS_PER_WEEK = 7; // guaranteed compile-time
cout << PI << "\n" << MAX_USERS << "\n" << GREETING
<< "\n" << DAYS_PER_WEEK;
return 0;
}
Constant Rules
Key rules about constants:
- A `const` object must be initialized (for class types, a default constructor still counts as initialization).
- `const` objects cannot be modified after initialization.
- `const` ≠ compile-time constant. Only constant expressions (e.g., `constexpr` or `const` integral initialized with a constant expression) can be used where a compile-time value is required.
- Namespace-scope `const` objects have internal linkage by default. To share a constant across translation units, use `extern const` with a single definition, or prefer `inline constexpr` (C++17+).
#include <array>
#include <iostream>
using namespace std;
int runtime_value();
int main() {
const int HOURS_PER_DAY = 24; // const, and also a constant expression
constexpr int DAYS = 7; // constexpr (compile-time)
std::array<int, DAYS> a = {}; // OK: requires compile-time size
const int RUNTIME = runtime_value(); // const but NOT a constant expression
// std::array<int, RUNTIME> b = {}; // ❌ Error: not a compile-time constant
cout << a.size();
}
Constants in Classes
`const` data members must be initialized by a default member initializer or in the constructor's member-initializer list.
#include <string>
#include <utility>
using namespace std;
class Widget {
const int id;
const string name;
public:
Widget(int i, string n) : id(i), name(std::move(n)) {}
int getId() const { return id; }
const string& getName() const { return name; }
};
int main() {
Widget w(42, "gear");
// w.id = 7; // ❌ Error: cannot assign to const member
}
Pointers and Const
Const with pointers can apply to the pointed-to value, the pointer itself, or both.
#include <iostream>
using namespace std;
int main() {
int x = 1, y = 2;
const int* p = &x; // pointer to const int (read-only via p)
// *p = 5; // ❌ cannot modify through p
p = &y; // OK: pointer itself can change
int* const q = &x; // const pointer to int
*q = 7; // OK: modify x via q
// q = &y; // ❌ cannot change where q points
const int* const r = &y; // const pointer to const int
// *r = 9; // ❌
// r = &x; // ❌
cout << x << " " << y;
}
7 2
When to Use Constants
Use constants for values that should not change during program execution, such as:
- Mathematical constants (`PI`, `E`)
- Configuration values (`MAX_CONNECTIONS`)
- Conversion factors (`KM_PER_MILE`)
- Domain-specific fixed values
#include <iostream>
using namespace std;
int main() {
constexpr double TAX_RATE = 0.15; // compile-time and self-documenting
double subtotal = 100;
double total = subtotal * (1 + TAX_RATE);
cout << total << '\n';
// Without a named constant (less clear):
double total2 = subtotal * 1.15;
cout << total2;
return 0;
}