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
EasyYou'll organize your code across three files:
MathUtils.h: Define your compile-time math functions.Create the following functions:
cube— aconstexprfunction that takes anintand returns its cube (n * n * n)triangularNumber— aconstexprfunction that calculates the nth triangular number using the formula n * (n + 1) / 2. This must always be evaluated at compile time.sumOfSquares— aconstexprfunction 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
Configstruct with aconstexprconstructor that takes three integers:width,height, anddepth. Store these as public members. Also add aconstexprmethod calledvolume()that returns width * height * depth.Below the struct, create a
constexprglobal constant calledDEFAULT_CONFIGinitialized with values 10, 20, and 5.main.cpp: Read two integers from input representing runtime values.First, demonstrate compile-time evaluation by creating
constexprvariables:- 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
constexprfunctions 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]
- Store
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: 25With inputs 5 and 12:
Cube of 4: 64
10th triangular number: 55
Default volume: 1000
Cube of 5: 125
Sum of squares: 169Cheat 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 runtimeA 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 constantYou 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 timeTry 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;
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesC++ Build & CompilationHeader Files & Source FilesNamespaces & ScopeIntroduction to OOP in C++Classes vs ObjectsThe 'this' PointerMethods (Member Functions)Attributes (Data Members)Ctors & Dtors BasicsRecap - Simple Calculator4Class Properties
Instance vs Static MembersGetters and SettersConst Member FunctionsMutable KeywordStatic Methods and VariablesFriend Functions & ClassesRecap - Bank Account Manager7Inheritance
Basic InheritanceInheritance Access LevelsCtor & Dtor Call OrderMethod OverridingVirtual Functions & VTableMultiple InheritanceVirtual InheritanceRecap - Employee Hierarchy2Memory Management
Stack vs Heap MemoryPointers and ReferencesDynamic Memory (new/delete)Smart Pointers in C++RAII in C++Recap - Dynamic Array Manager5Encapsulation
Access Specifiers in C++Access Specifiers In DepthInformation HidingStruct vs ClassNested & Inner ClassesRecap - Student Records System8Polymorphism
Compile vs Runtime PolymorphFunction OverloadingVirtual Functions RevisitedPure Virtual FunctionsAbstract ClassesInterface Design in C++Dynamic Casting & RTTIRecap - Shape Calculator3Constructors & Destructors
Default ConstructorParameterized ConstructorCopy ConstructorMove ConstructorConstructor Init ListsDelegating ConstructorsDestructor Deep DiveRule of Three / Five / ZeroRecap - String Class6Operator Overloading
Intro to Operator OverloadArithmetic Operator OverloadComparison Operator OverloadStream OperatorsAssignment Operator Overload[] and () Operator OverloadType Conversion OperatorsRecap - Matrix Class9Templates
Function TemplatesClass TemplatesTemplate SpecializationVariadic TemplatesSFINAE & Type Traits BasicsRecap - Generic Container12Modern C++ Features
Move Semantics & RvaluesPerfect ForwardingLambda Expressions In Depthstd::function & std::bindconstexpr and constevalStructured Bindingsoptional, variant, any