Constants, Literals, and Constexpr in C++
A constant is a value that cannot be changed while the program is running. In C++, there are several ways to declare a constant: using const, constexpr, #define (C style), and literals. Understanding the differences between these methods is important for writing code that is safe, efficient, and easy to maintain.
Why Use Constants?
- Safety — Prevents accidental changes to a value
- Readability — A constant name is more meaningful than a raw number (magic number)
- Maintainability — Value changes are made in only one place
- Optimization — The compiler can optimize code with constant values
Code Example
#include <iostream>
#include <cmath>
using namespace std;
int main() {
// Using const
const double PI = 3.14159265358979;
const int MAX_LIMIT = 100;
const string APP_NAME = "Calculator";
// Using constexpr (evaluated at compile time)
constexpr double GRAVITY = 9.80665;
constexpr int BUFFER_SIZE = 1024;
// Using literals directly
double radius = 5.0;
double circleArea = PI * radius * radius;
cout << "=== " << APP_NAME << " ===" << endl;
cout << "Circle area (r=" << radius << "): " << circleArea << endl;
cout << "Gravity: " << GRAVITY << " m/s²" << endl;
// Literals in various bases
cout << "\n=== Literals in Various Bases ===" << endl;
int decimal = 42;
int octal = 052;
int hexadecimal = 0x2A;
int binary = 0b101010;
cout << "Decimal: " << decimal << endl;
cout << "Octal (052): " << octal << endl;
cout << "Hexadecimal (0x2A): " << hexadecimal << endl;
cout << "Binary (0b101010): " << binary << endl;
// Character literals
char letter = 'A';
char newline = '\n';
wchar_t omega = L'Ω';
// String literals
const char* cText = "Hello C string";
string cppText = "Hello C++ string";
const wchar_t* wideText = L"Wide text";
// Boolean literal
bool valid = true;
bool invalid = false;
// Null pointer literal
int* ptr = nullptr;
return 0;
}
Code Explanation
1. The const Keyword
const declares a variable whose value cannot be changed after initialization.
const int LIMIT = 100;
LIMIT = 200; // ❌ Error: assignment to a const variable
Characteristics of const:
- Evaluated at runtime (when the program is running)
- Can be used with any data type
- Must be initialized at declaration
- Stored in regular memory (its address can be taken)
- Can be used for class members
// const with various data types
const int size = 10;
const double pi = 3.14159;
const string name = "John";
const char* text = "Hello";
// const on pointers
int value = 10;
int* const fixedPtr = &value; // Fixed pointer (address cannot be changed)
const int* constPtr = &value; // Pointed-to data is constant (value cannot be changed)
const int* const fixedConstPtr = &value; // Both are fixed
// const on references
const int& ref = value; // Constant reference — value cannot be changed through ref
2. The constexpr Keyword (C++11)
constexpr declares an expression or function that is evaluated at compile time.
constexpr int SIZE = 100; // Evaluated at compile time
constexpr double PI = 3.14159; // Evaluated at compile time
constexpr int SQUARE(int x) { // constexpr function
return x * x;
}
constexpr int result = SQUARE(10); // Computed at compile time
Characteristics of constexpr:
- Evaluated at compile time
- The value must be determinable at compile time
- Faster (because it is already computed before the program runs)
- Can be used for array sizes, template arguments, and more
- constexpr variables are implicitly const
Differences Between const and constexpr:
| Aspect | const |
constexpr |
|---|---|---|
| Evaluation | Runtime | Compile time |
| Value | Can depend on runtime | Must be known at compile time |
| Speed | Normal | Faster (pre-computed) |
| Usage | Variables, parameters, members | Variables, functions, constructors |
| Memory | Regular memory | Can be initialized in .rodata |
3. Preprocessor #define (C Style)
#define is a C-language preprocessor directive that replaces text literally before compilation.
#define PI 3.14159
#define MAX_LIMIT 100
#define CIRCLE_AREA(x) (PI * (x) * (x))
Note: In C++,
#defineshould be avoided because it has no type safety and can cause hard-to-find bugs. Useconstorconstexprinstead.
Problems with #define:
#define SQUARE(x) x * x
int result = SQUARE(3 + 2);
// The preprocessor turns it into: 3 + 2 * 3 + 2 = 3 + 6 + 2 = 11
// NOT the expected 25!
// Solution with constexpr
constexpr int square(int x) { return x * x; }
int result = square(3 + 2); // 25 — correct!
Literals
A literal is a constant value written directly in the program code. Literals are the most basic representation of data in C++.
Numeric Literals
Decimal (Base 10)
int a = 42;
int b = -17;
double c = 3.14;
Octal (Base 8)
An octal literal starts with a zero 0.
int octal = 052; // = 42 in decimal
int negativeOctal = -017; // = -15 in decimal
Hexadecimal (Base 16)
A hexadecimal literal starts with 0x or 0X.
int hexa = 0x2A; // = 42 in decimal
int hexaUpper = 0xFF; // = 255 in decimal
int hexaLower = 0xff; // Also = 255 (case-insensitive)
Binary (Base 2, C++14)
A binary literal starts with 0b or 0B.
int binary = 0b101010; // = 42 in decimal
int negativeBinary = -0b1100; // = -12 in decimal
Digit Separator (C++14)
For making long numbers easier to read:
int million = 1'000'000; // = 1000000
double pi = 3.141'592'653'589'793; // Easier to read
int binary = 0b1010'0011'1100'0101; // Long binary number
int hexa = 0xFF'FF'FF'FF; // Hexadecimal
Character Literals
char a = 'A'; // Regular character
char b = '\n'; // Escape sequence
char c = '\t'; // Tab
char d = '\\'; // Backslash
char e = '\''; // Single quote
char f = '\0'; // Null character
// Unicode character literals
char16_t unicode16 = u'Ω'; // UTF-16
char32_t unicode32 = U'Ω'; // UTF-32
wchar_t wide = L'Ω'; // Wide character
String Literals
// C-style string
const char* text1 = "Hello, World!";
// C++ string
string text2 = "Hello, C++!";
// Raw string literal (C++11) — without escape sequences
const char* path = R"(C:\Users\Documents\file.txt)";
const char* multiLine = R"(First line
Second line
Third line)";
// Strings with encoding prefix
const char* utf8 = u8"Welcome"; // UTF-8
const wchar_t* wide_str = L"Wide text"; // Wide string
const char16_t* utf16 = u"Unicode"; // UTF-16
const char32_t* utf32 = U"Unicode 32"; // UTF-32
Boolean Literals
bool a = true;
bool b = false;
Nullptr Literal (C++11)
int* ptr = nullptr; // Safe null pointer
double* dptr = nullptr; // Works for all pointer types
User-Defined Literals (C++11)
C++ allows you to define your own literals with custom suffixes:
#include <iostream>
#include <string>
using namespace std;
// Literal for Celsius temperature
constexpr long double operator"" _celsius(long double celsius) {
return celsius;
}
// Literal for distance in meters
constexpr long double operator"" _meter(long double meter) {
return meter;
}
// Literal for conversion to Fahrenheit
constexpr long double operator"" _fahrenheit(long double celsius) {
return celsius * 9.0 / 5.0 + 32.0;
}
// Literal for strings
string operator"" _s(const char* str, size_t len) {
return string(str, len);
}
int main() {
auto temperature = 36.5_celsius;
auto distance = 100.0_meter;
auto fahrenheit = 36.5_fahrenheit;
auto name = "John"_s;
cout << "Temperature: " << temperature << " °C" << endl;
cout << "Distance: " << distance << " m" << endl;
cout << "Fahrenheit: " << fahrenheit << " °F" << endl;
cout << "Name: " << name << endl;
return 0;
}
Custom literals are very useful for making code more expressive and type-safe, for example in unit systems (units) or formatting.
Comparing Constant Methods
| Method | Compile-time | Type-safe | Scope-aware | Debuggable |
|---|---|---|---|---|
#define |
Yes (preprocessor) | No | No | No |
const |
No (runtime) | Yes | Yes | Yes |
constexpr |
Yes | Yes | Yes | Yes |
enum |
Partial | Yes | Yes | Partial |
Recommendations
- Use
constexprfor values that can be computed at compile time - Use
constfor values that can only be determined at runtime - Avoid
#definefor constants — useconstorconstexpr - Use
enum classfor a set of related values
Everyday Use Cases
Application Configuration with Constants
#include <iostream>
#include <string>
#include <cmath>
using namespace std;
// Application constants using constexpr
constexpr int PAGE_SIZE = 20;
constexpr int PASSWORD_LIMIT = 8;
constexpr double DEFAULT_TAX = 0.11;
constexpr const char* APP_VERSION = "2.1.0";
// constexpr function for compile-time computation
constexpr double calculateCircleArea(double r) {
return 3.14159265358979 * r * r;
}
int main() {
// Using literals for input
double radius = 7.0;
double area = calculateCircleArea(radius);
cout << "=== Application Configuration ===" << endl;
cout << "Version: " << APP_VERSION << endl;
cout << "Page size: " << PAGE_SIZE << endl;
cout << "Default tax: " << (DEFAULT_TAX * 100) << "%" << endl;
cout << "\n=== Calculation ===" << endl;
cout << "Circle area (r=" << radius << "): " << area << endl;
// Literals with digit separators
long long population = 275'501'339;
double gdp = 1'319'100'000'000.00;
cout << "\n=== Indonesia Data ===" << endl;
cout << "Population: " << population << endl;
cout << "GDP: $ " << gdp << endl;
// constexpr for array size
constexpr int NUM_STUDENTS = 5;
int scores[NUM_STUDENTS] = {85, 90, 78, 92, 88};
double total = 0;
for (int i = 0; i < NUM_STUDENTS; i++) {
total += scores[i];
}
double average = total / NUM_STUDENTS;
cout << "\n=== Score Data ===" << endl;
cout << "Average: " << average << endl;
return 0;
}
Avoiding Magic Numbers
// ❌ Bad: magic numbers
void processPayment(double amount) {
double tax = amount * 0.11;
double discount = amount > 1000000 ? amount * 0.05 : 0;
if (amount < 50000) {
cout << "Minimum payment not met" << endl;
}
}
// ✅ Good: using constants
constexpr double TAX_PERCENT = 0.11;
constexpr double DISCOUNT_PERCENT = 0.05;
constexpr double DISCOUNT_LIMIT = 1000000.0;
constexpr double MINIMUM_PAYMENT = 50000.0;
void processPayment(double amount) {
double tax = amount * TAX_PERCENT;
double discount = amount > DISCOUNT_LIMIT ? amount * DISCOUNT_PERCENT : 0;
if (amount < MINIMUM_PAYMENT) {
cout << "Minimum payment not met" << endl;
}
}
Conclusion
C++ provides several ways to declare a constant: const for runtime, constexpr for compile time, and #define (which should be avoided). Literals are constant values written directly in code, available in various formats: decimal, octal (0), hexadecimal (0x), binary (0b), character, string, and boolean. User-defined literals allow creating custom literals for more expressive code. Use constexpr wherever possible for optimal performance, and const for values that are only known at runtime.