Coming Soon
This lesson is currently being developed
Constexpr variables
Master compile-time constants with constexpr.
What to Expect
Comprehensive explanations with practical examples
Interactive coding exercises to practice concepts
Knowledge quiz to test your understanding
Step-by-step guidance for beginners
Development Status
Content is being carefully crafted to provide the best learning experience
Preview
Early Preview Content
This content is still being developed and may change before publication.
5.6 — Constexpr variables
In this lesson, you'll learn about constexpr
variables in C++, understand how they extend constant expressions, and discover when to use them for compile-time evaluation.
What are constexpr variables?
A constexpr variable is a variable that must be initialized with a constant expression and whose value can be used in contexts that require compile-time constants. The constexpr
keyword is stronger than const
- it guarantees that the value is computed at compile time.
Think of constexpr
as a promise to the compiler: "This value will definitely be known when you compile the program, and you can use it anywhere you need a compile-time constant."
constexpr vs const
While both const
and constexpr
create constants, they have important differences:
#include <iostream>
int getValue()
{
return 42;
}
int main()
{
const int a = 5; // ✓ const with compile-time value
const int b = getValue(); // ✓ const with runtime value
constexpr int c = 5; // ✓ constexpr with compile-time value
// constexpr int d = getValue(); // ❌ Error: getValue() not constexpr
std::cout << "const values: " << a << ", " << b << std::endl;
std::cout << "constexpr value: " << c << std::endl;
return 0;
}
Output:
const values: 5, 42
constexpr value: 5
Key differences:
const
variables can be initialized at runtimeconstexpr
variables must be initialized with constant expressionsconstexpr
variables can be used anywhere a constant expression is required
Basic constexpr variable syntax
To declare a constexpr variable:
constexpr type variable_name = constant_expression;
Examples of valid constexpr variables:
#include <iostream>
int main()
{
// Basic constexpr variables
constexpr int maxSize = 100;
constexpr double pi = 3.141592653589793;
constexpr bool isDebug = true;
constexpr char newline = '\n';
// constexpr with calculations
constexpr int doubled = maxSize * 2;
constexpr double area = pi * 5.0 * 5.0; // Circle with radius 5
constexpr int result = isDebug ? 1 : 0;
std::cout << "Max size: " << maxSize << std::endl;
std::cout << "Pi: " << pi << std::endl;
std::cout << "Doubled: " << doubled << std::endl;
std::cout << "Circle area: " << area << std::endl;
std::cout << "Debug flag as int: " << result << std::endl;
return 0;
}
Output:
Max size: 100
Pi: 3.14159
Doubled: 200
Circle area: 78.5398
Debug flag as int: 1
Using constexpr variables for array sizes
One major advantage of constexpr
is that these variables can be used to specify array sizes:
#include <iostream>
int main()
{
// constexpr variables for array dimensions
constexpr int width = 10;
constexpr int height = 8;
constexpr int totalCells = width * height;
// Can use constexpr variables for array sizes
int grid[totalCells];
int row[width];
int column[height];
// Initialize the grid with sequential numbers
for (int i = 0; i < totalCells; ++i)
{
grid[i] = i + 1;
}
std::cout << "Grid dimensions: " << width << "x" << height << std::endl;
std::cout << "Total cells: " << totalCells << std::endl;
std::cout << "First few grid values: ";
for (int i = 0; i < 10; ++i)
{
std::cout << grid[i] << " ";
}
std::cout << std::endl;
// Display as a grid
std::cout << "\nGrid layout:" << std::endl;
for (int row = 0; row < height; ++row)
{
for (int col = 0; col < width; ++col)
{
std::cout << grid[row * width + col] << "\t";
}
std::cout << std::endl;
}
return 0;
}
Output:
Grid dimensions: 10x8
Total cells: 80
First few grid values: 1 2 3 4 5 6 7 8 9 10
Grid layout:
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
31 32 33 34 35 36 37 38 39 40
41 42 43 44 45 46 47 48 49 50
51 52 53 54 55 56 57 58 59 60
61 62 63 64 65 66 67 68 69 70
71 72 73 74 75 76 77 78 79 80
Complex constexpr calculations
constexpr variables can involve complex calculations, as long as they can be evaluated at compile time:
#include <iostream>
int main()
{
// Mathematical constants
constexpr double pi = 3.141592653589793;
constexpr double e = 2.718281828459045;
// Geometric calculations
constexpr double radius = 7.0;
constexpr double diameter = 2.0 * radius;
constexpr double circumference = 2.0 * pi * radius;
constexpr double area = pi * radius * radius;
constexpr double sphereVolume = (4.0 / 3.0) * pi * radius * radius * radius;
// Unit conversions
constexpr double inchesToCm = 2.54;
constexpr double feetToMeters = 0.3048;
constexpr double radiusInCm = radius * inchesToCm;
// Time calculations
constexpr int secondsPerMinute = 60;
constexpr int minutesPerHour = 60;
constexpr int hoursPerDay = 24;
constexpr int secondsPerDay = secondsPerMinute * minutesPerHour * hoursPerDay;
std::cout << "Circle with radius " << radius << " inches:" << std::endl;
std::cout << "Diameter: " << diameter << " inches" << std::endl;
std::cout << "Circumference: " << circumference << " inches" << std::endl;
std::cout << "Area: " << area << " square inches" << std::endl;
std::cout << "Sphere volume: " << sphereVolume << " cubic inches" << std::endl;
std::cout << "Radius in cm: " << radiusInCm << " cm" << std::endl;
std::cout << "Seconds per day: " << secondsPerDay << std::endl;
return 0;
}
Output:
Circle with radius 7 inches:
Diameter: 14 inches
Circumference: 43.9823 inches
Area: 153.938 square inches
Sphere volume: 1436.76 cubic inches
Radius in cm: 17.78 cm
Seconds per day: 86400
constexpr with different data types
constexpr works with all fundamental data types:
#include <iostream>
int main()
{
// Integer constexpr variables
constexpr int maxPlayers = 4;
constexpr long long bigNumber = 1000000000000LL;
constexpr unsigned int flags = 0xFF;
// Floating-point constexpr variables
constexpr float gravity = 9.81f;
constexpr double precision = 1e-10;
// Character constexpr variables
constexpr char separator = '|';
constexpr char escapeChar = '\\';
// Boolean constexpr variables
constexpr bool isEnabled = true;
constexpr bool isProduction = false;
// Calculations with mixed types
constexpr double timeStep = 1.0 / 60.0; // 60 FPS
constexpr int bufferSize = maxPlayers * 1024;
constexpr bool hasEnoughPlayers = maxPlayers >= 2;
std::cout << "Game Configuration:" << std::endl;
std::cout << "Max players: " << maxPlayers << std::endl;
std::cout << "Buffer size: " << bufferSize << " bytes" << std::endl;
std::cout << "Time step: " << timeStep << " seconds" << std::endl;
std::cout << "Has enough players: " << hasEnoughPlayers << std::endl;
std::cout << "Gravity: " << gravity << " m/s²" << std::endl;
std::cout << "Separator: '" << separator << "'" << std::endl;
return 0;
}
Output:
Game Configuration:
Max players: 4
Buffer size: 4096 bytes
Time step: 0.0166667 seconds
Has enough players: 1
Gravity: 9.81 m/s²
Separator: '|'
Using constexpr variables in switch statements
constexpr variables can be used as case labels in switch statements:
#include <iostream>
int main()
{
// Game state constants
constexpr int menuState = 0;
constexpr int playingState = 1;
constexpr int pausedState = 2;
constexpr int gameOverState = 3;
// Current game state
int currentState = playingState;
switch (currentState)
{
case menuState:
std::cout << "Showing main menu" << std::endl;
break;
case playingState:
std::cout << "Game is running" << std::endl;
break;
case pausedState:
std::cout << "Game is paused" << std::endl;
break;
case gameOverState:
std::cout << "Game over screen" << std::endl;
break;
default:
std::cout << "Unknown game state" << std::endl;
break;
}
// Show all possible states
std::cout << "\nGame states:" << std::endl;
std::cout << "Menu: " << menuState << std::endl;
std::cout << "Playing: " << playingState << std::endl;
std::cout << "Paused: " << pausedState << std::endl;
std::cout << "Game Over: " << gameOverState << std::endl;
return 0;
}
Output:
Game is running
Game states:
Menu: 0
Playing: 1
Paused: 2
Game Over: 3
constexpr with bit operations
constexpr is particularly useful for bit manipulation constants:
#include <iostream>
int main()
{
// Bit flag constants
constexpr unsigned int readFlag = 1 << 0; // 0001
constexpr unsigned int writeFlag = 1 << 1; // 0010
constexpr unsigned int executeFlag = 1 << 2; // 0100
constexpr unsigned int deleteFlag = 1 << 3; // 1000
// Combined permissions
constexpr unsigned int readWrite = readFlag | writeFlag;
constexpr unsigned int allPermissions = readFlag | writeFlag | executeFlag | deleteFlag;
constexpr unsigned int noPermissions = 0;
// Color constants (ARGB format)
constexpr unsigned int alphaMask = 0xFF000000;
constexpr unsigned int redMask = 0x00FF0000;
constexpr unsigned int greenMask = 0x0000FF00;
constexpr unsigned int blueMask = 0x000000FF;
// Predefined colors
constexpr unsigned int red = alphaMask | redMask;
constexpr unsigned int green = alphaMask | greenMask;
constexpr unsigned int blue = alphaMask | blueMask;
constexpr unsigned int white = alphaMask | redMask | greenMask | blueMask;
constexpr unsigned int black = alphaMask;
std::cout << "File Permissions:" << std::endl;
std::cout << "Read: " << readFlag << std::endl;
std::cout << "Write: " << writeFlag << std::endl;
std::cout << "Execute: " << executeFlag << std::endl;
std::cout << "Delete: " << deleteFlag << std::endl;
std::cout << "Read+Write: " << readWrite << std::endl;
std::cout << "All permissions: " << allPermissions << std::endl;
std::cout << "\nColors (hex):" << std::endl;
std::cout << "Red: 0x" << std::hex << red << std::endl;
std::cout << "Green: 0x" << green << std::endl;
std::cout << "Blue: 0x" << blue << std::endl;
std::cout << "White: 0x" << white << std::endl;
std::cout << "Black: 0x" << black << std::endl;
return 0;
}
Output:
File Permissions:
Read: 1
Write: 2
Execute: 4
Delete: 8
Read+Write: 3
All permissions: 15
Colors (hex):
Red: 0xffff0000
Green: 0xff00ff00
Blue: 0xff0000ff
White: 0xffffffff
Black: 0xff000000
Practical example: Game configuration
Here's a practical example using constexpr for game configuration:
#include <iostream>
int main()
{
// Screen configuration
constexpr int screenWidth = 1920;
constexpr int screenHeight = 1080;
constexpr int bitsPerPixel = 32;
constexpr int bytesPerPixel = bitsPerPixel / 8;
// Calculated screen properties
constexpr int totalPixels = screenWidth * screenHeight;
constexpr int screenBufferSize = totalPixels * bytesPerPixel;
constexpr double aspectRatio = static_cast<double>(screenWidth) / screenHeight;
// Game world configuration
constexpr int tileSize = 32;
constexpr int tilesWide = screenWidth / tileSize;
constexpr int tilesHigh = screenHeight / tileSize;
constexpr int totalTiles = tilesWide * tilesHigh;
// Player configuration
constexpr int maxPlayers = 4;
constexpr int playerStartingHealth = 100;
constexpr double playerSpeed = 5.5;
constexpr int playerInventorySlots = 20;
// Game timing (60 FPS)
constexpr double targetFPS = 60.0;
constexpr double frameTime = 1.0 / targetFPS;
constexpr int frameTimeMilliseconds = static_cast<int>(frameTime * 1000);
std::cout << "Game Configuration:" << std::endl;
std::cout << "==================" << std::endl;
std::cout << "\nScreen:" << std::endl;
std::cout << "Resolution: " << screenWidth << "x" << screenHeight << std::endl;
std::cout << "Aspect ratio: " << aspectRatio << ":1" << std::endl;
std::cout << "Total pixels: " << totalPixels << std::endl;
std::cout << "Buffer size: " << screenBufferSize << " bytes ("
<< screenBufferSize / 1024 / 1024 << " MB)" << std::endl;
std::cout << "\nWorld:" << std::endl;
std::cout << "Tile size: " << tileSize << "x" << tileSize << " pixels" << std::endl;
std::cout << "World size: " << tilesWide << "x" << tilesHigh << " tiles" << std::endl;
std::cout << "Total tiles: " << totalTiles << std::endl;
std::cout << "\nPlayers:" << std::endl;
std::cout << "Max players: " << maxPlayers << std::endl;
std::cout << "Starting health: " << playerStartingHealth << " HP" << std::endl;
std::cout << "Player speed: " << playerSpeed << " units/second" << std::endl;
std::cout << "Inventory slots: " << playerInventorySlots << std::endl;
std::cout << "\nTiming:" << std::endl;
std::cout << "Target FPS: " << targetFPS << std::endl;
std::cout << "Frame time: " << frameTime << " seconds" << std::endl;
std::cout << "Frame time: " << frameTimeMilliseconds << " milliseconds" << std::endl;
return 0;
}
Output:
Game Configuration:
==================
Screen:
Resolution: 1920x1080
Aspect ratio: 1.77778:1
Total pixels: 2073600
Buffer size: 8294400 bytes (7 MB)
World:
Tile size: 32x32 pixels
World size: 60x33 tiles
Total tiles: 1980
Players:
Max players: 4
Starting health: 100 HP
Player speed: 5.5 units/second
Inventory slots: 20
Timing:
Target FPS: 60
Frame time: 0.0166667 seconds
Frame time: 16 milliseconds
Best practices for constexpr variables
1. Use constexpr for compile-time constants
#include <iostream>
int main()
{
// Good: Use constexpr for values that should be known at compile time
constexpr int maxConnections = 100;
constexpr double pi = 3.141592653589793;
constexpr bool debugMode = true;
// Use these constants throughout your program
int connectionPool[maxConnections];
double circleArea = pi * 5.0 * 5.0;
if (debugMode)
{
std::cout << "Debug mode enabled" << std::endl;
}
std::cout << "Max connections: " << maxConnections << std::endl;
std::cout << "Circle area: " << circleArea << std::endl;
return 0;
}
2. Group related constexpr variables
#include <iostream>
int main()
{
// Group related constants together
// Network configuration
constexpr int maxClients = 50;
constexpr int bufferSize = 1024;
constexpr double timeoutSeconds = 30.0;
constexpr int retryAttempts = 3;
// Physics constants
constexpr double gravity = 9.81;
constexpr double airDensity = 1.225; // kg/m³ at sea level
constexpr double speedOfSound = 343.0; // m/s at 20°C
std::cout << "Network Settings:" << std::endl;
std::cout << "Max clients: " << maxClients << std::endl;
std::cout << "Buffer size: " << bufferSize << " bytes" << std::endl;
std::cout << "Timeout: " << timeoutSeconds << " seconds" << std::endl;
std::cout << "\nPhysics Constants:" << std::endl;
std::cout << "Gravity: " << gravity << " m/s²" << std::endl;
std::cout << "Air density: " << airDensity << " kg/m³" << std::endl;
std::cout << "Speed of sound: " << speedOfSound << " m/s" << std::endl;
return 0;
}
3. Use meaningful names
#include <iostream>
int main()
{
// Good: Descriptive names
constexpr int windowWidth = 800;
constexpr int windowHeight = 600;
constexpr int framesPerSecond = 60;
constexpr double metersToFeet = 3.28084;
// Derived values
constexpr int windowArea = windowWidth * windowHeight;
constexpr double frameInterval = 1.0 / framesPerSecond;
std::cout << "Window: " << windowWidth << "x" << windowHeight
<< " (" << windowArea << " pixels)" << std::endl;
std::cout << "Frame rate: " << framesPerSecond
<< " FPS (every " << frameInterval << " seconds)" << std::endl;
std::cout << "Conversion: 1 meter = " << metersToFeet << " feet" << std::endl;
return 0;
}
When to use constexpr vs const
Choose constexpr
when:
- The value must be known at compile time
- You need to use the variable for array sizes
- You want to use the variable in template parameters
- You need it for switch case labels
Choose const
when:
- The value might be computed at runtime
- You just want to prevent modification
- The initial value comes from user input or file reading
#include <iostream>
int getUserInput()
{
// Simulate getting user input
return 42;
}
int main()
{
// constexpr: Must be compile-time constant
constexpr int arraySize = 100;
constexpr double pi = 3.14159;
// const: Can be runtime value
const int userValue = getUserInput();
const int currentYear = 2024; // Could also be constexpr
// Use constexpr for array size
int numbers[arraySize];
std::cout << "Array size (constexpr): " << arraySize << std::endl;
std::cout << "User value (const): " << userValue << std::endl;
std::cout << "Pi (constexpr): " << pi << std::endl;
std::cout << "Current year (const): " << currentYear << std::endl;
return 0;
}
Output:
Array size (constexpr): 100
User value (const): 42
Pi (constexpr): 3.14159
Current year (const): 2024
Summary
constexpr variables are a powerful feature for creating compile-time constants:
- Stronger than const: Must be initialized with constant expressions
- Compile-time evaluation: Values are computed when the program is compiled
- Versatile usage: Can be used for array sizes, template parameters, switch cases
- Better performance: No runtime computation needed
- Type safety: Works with all fundamental data types
- Best practices: Use meaningful names, group related constants, choose constexpr when compile-time evaluation is needed
constexpr variables are essential for modern C++ programming, providing both performance benefits and compile-time safety. They're particularly useful for configuration values, mathematical constants, and any scenario where you need guaranteed compile-time constants.
Quiz
- What's the main difference between
const
andconstexpr
variables? - Can a
constexpr
variable be initialized with a function call to a regular (non-constexpr) function? - In what contexts can
constexpr
variables be used thatconst
variables cannot? - When are
constexpr
variables evaluated - at compile time or runtime? - Which declaration is correct for a constexpr variable?
a) constexpr int x = getValue(); // getValue() returns int b) constexpr int x = 5 + 3; c) constexpr int x; d) Both b and c
Practice exercises
Try these constexpr-related exercises:
- Create a set of constexpr variables for a 2D game (screen dimensions, tile sizes, player stats) and use them to calculate derived values
- Use constexpr variables to create bit flag constants for a permissions system
- Create constexpr variables for mathematical constants and use them in geometric calculations
- Replace the magic numbers in this code with appropriate constexpr variables:
int buffer[1024]; double area = 3.14159 * 10 * 10; if (score > 1000) { cout << "High score!"; }
Explore More Courses
Discover other available courses while this lesson is being prepared.
Browse CoursesLesson Discussion
Share your thoughts and questions