Menu
Coddy logo textTech

constexpr and consteval

Part of the Object Oriented Programming section of Coddy's C++ journey — lesson 87 of 104.

C++ allows computations to happen at compile time rather than runtime, which can significantly improve performance. The constexpr keyword (C++11) and consteval keyword (C++20) give you control over when expressions are evaluated.

A constexpr function can be evaluated at compile time if given constant arguments, but it can also run at runtime with non-constant inputs:

#include <iostream>

constexpr int square(int n) {
    return n * n;
}

int main() {
    constexpr int compileTime = square(5);  // Evaluated at compile time
    
    int x = 7;
    int runtime = square(x);  // Evaluated at runtime
    
    std::cout << compileTime << "\n";  // 25
    std::cout << runtime << "\n";      // 49
}

When you need to guarantee compile-time evaluation, use consteval. A consteval function must produce a constant - calling it with runtime values is a compilation error:

consteval int factorial(int n) {
    return n <= 1 ? 1 : n * factorial(n - 1);
}

int main() {
    constexpr int result = factorial(5);  // OK: 120 computed at compile time
    
    // int x = 5;
    // int bad = factorial(x);  // ERROR: x is not a constant
}

You can also use constexpr with variables and even class constructors, enabling entire objects to be created at compile time. This is particularly useful for lookup tables, configuration values, or any data that doesn't change during program execution.

challenge icon

Challenge

Easy

You'll organize your code across three files:

  • MathUtils.h: Define your compile-time math functions.

    Create the following functions:

    • cube — a constexpr function that takes an int and returns its cube (n * n * n)
    • triangularNumber — a constexpr function that calculates the nth triangular number using the formula n * (n + 1) / 2. This must always be evaluated at compile time.
    • sumOfSquares — a constexpr function that takes two integers and returns the sum of their squares (a*a + b*b)
  • Config.h: Create a configuration structure using compile-time constants.

    Define a Config struct with a constexpr constructor that takes three integers: width, height, and depth. Store these as public members. Also add a constexpr method called volume() that returns width * height * depth.

    Below the struct, create a constexpr global constant called DEFAULT_CONFIG initialized with values 10, 20, and 5.

  • main.cpp: Read two integers from input representing runtime values.

    First, demonstrate compile-time evaluation by creating constexpr variables:

    • Store cube(4) in a constexpr variable and print: Cube of 4: [value]
    • Store triangularNumber(10) in a constexpr variable and print: 10th triangular number: [value]
    • Print the default config's volume: Default volume: [value]

    Then, demonstrate that constexpr functions can also work at runtime by using your two input values:

    • Call cube() with the first input and print: Cube of [input]: [result]
    • Call sumOfSquares() with both inputs and print: Sum of squares: [result]

For example, with inputs 3 and 4:

Cube of 4: 64
10th triangular number: 55
Default volume: 1000
Cube of 3: 27
Sum of squares: 25

With inputs 5 and 12:

Cube of 4: 64
10th triangular number: 55
Default volume: 1000
Cube of 5: 125
Sum of squares: 169

Cheat sheet

C++ allows computations at compile time using constexpr (C++11) and consteval (C++20) keywords.

A constexpr function can be evaluated at compile time with constant arguments, but also runs at runtime with non-constant inputs:

constexpr int square(int n) {
    return n * n;
}

constexpr int compileTime = square(5);  // Evaluated at compile time

int x = 7;
int runtime = square(x);  // Evaluated at runtime

A consteval function must be evaluated at compile time. Calling it with runtime values causes a compilation error:

consteval int factorial(int n) {
    return n <= 1 ? 1 : n * factorial(n - 1);
}

constexpr int result = factorial(5);  // OK: computed at compile time

// int x = 5;
// int bad = factorial(x);  // ERROR: x is not a constant

You can use constexpr with variables, class constructors, and methods to enable compile-time object creation:

struct Config {
    int width, height, depth;
    
    constexpr Config(int w, int h, int d) 
        : width(w), height(h), depth(d) {}
    
    constexpr int volume() const {
        return width * height * depth;
    }
};

constexpr Config config(10, 20, 5);  // Created at compile time

Try it yourself

#include <iostream>
#include "MathUtils.h"
#include "Config.h"

using namespace std;

int main() {
    // Read two integers from input
    int input1, input2;
    cin >> input1;
    cin >> input2;
    
    // TODO: Demonstrate compile-time evaluation
    // Create a constexpr variable storing cube(4) and print: "Cube of 4: [value]"
    
    // TODO: Create a constexpr variable storing triangularNumber(10)
    // and print: "10th triangular number: [value]"
    
    // TODO: Print the default config's volume: "Default volume: [value]"
    
    // TODO: Demonstrate runtime usage of constexpr functions
    // Call cube() with input1 and print: "Cube of [input1]: [result]"
    
    // TODO: Call sumOfSquares() with both inputs
    // and print: "Sum of squares: [result]"
    
    return 0;
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming