Coming Soon

This lesson is currently being developed

Constexpr variables

Master compile-time constants with constexpr.

Constants and Strings
Chapter
Beginner
Difficulty
40min
Estimated Time

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

In Progress

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 runtime
  • constexpr variables must be initialized with constant expressions
  • constexpr 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

  1. What's the main difference between const and constexpr variables?
  2. Can a constexpr variable be initialized with a function call to a regular (non-constexpr) function?
  3. In what contexts can constexpr variables be used that const variables cannot?
  4. When are constexpr variables evaluated - at compile time or runtime?
  5. 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:

  1. Create a set of constexpr variables for a 2D game (screen dimensions, tile sizes, player stats) and use them to calculate derived values
  2. Use constexpr variables to create bit flag constants for a permissions system
  3. Create constexpr variables for mathematical constants and use them in geometric calculations
  4. 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!";
    }
    

Continue Learning

Explore other available lessons while this one is being prepared.

View Course

Explore More Courses

Discover other available courses while this lesson is being prepared.

Browse Courses

Lesson Discussion

Share your thoughts and questions

💬

No comments yet. Be the first to share your thoughts!

Sign in to join the discussion