Search Knowledge

© 2026 LIBREUNI PROJECT

Modern C++ Programming / Modern C++20/23

Modules (C++20)

Modules: Modern Code Organization

C++20 modules replace the preprocessor-based #include system with a semantic import mechanism, offering faster compilation and better encapsulation.

Basic Module Syntax

Module Interface

// math.cppm (module interface file)
export module math;

export int add(int a, int b) {
    return a + b;
}

export int subtract(int a, int b) {
    return a - b;
}

Importing a Module

// main.cpp
import math;

int main() {
    auto result = add(5, 3);  // Uses exported add()
    return 0;
}

Export Declarations

Exporting Functions

export module geometry;

export namespace geometry {
    double circle_area(double radius);
    double square_area(double side);
}

Exporting Classes

export module shapes;

export class Circle {
    double radius_;
public:
    Circle(double r) : radius_(r) {}
    double area() const;
};

// Implementation can be in same file or separate
double Circle::area() const {
    return 3.14159 * radius_ * radius_;
}

Exporting Templates

export module containers;

export template<typename T>
class Stack {
    std::vector<T> data_;
public:
    void push(const T& value) { data_.push_back(value); }
    T pop();
};

Module Partitions

Split large modules into partitions:

// math-basics.cppm (partition interface)
export module math:basics;

export int add(int a, int b) {
    return a + b;
}

// math-advanced.cppm (partition interface)
export module math:advanced;

export double sqrt(double x) {
    // implementation
}

// math.cppm (primary module interface)
export module math;

export import :basics;    // Re-export partition
export import :advanced;

Implementation Units

Separate interface from implementation:

// widget.cppm (module interface)
export module widget;

export class Widget {
public:
    void process();
private:
    void internal_helper();
};

// widget.cpp (module implementation)
module widget;  // No 'export'

void Widget::process() {
    internal_helper();
}

void Widget::internal_helper() {
    // Implementation details
}

Module Linkage

Exported vs Non-Exported

export module utils;

// Exported: Visible to importers
export void public_function();

// Not exported: Module-internal only
void private_function();

namespace {
    // Internal linkage
    void internal_function();
}

Global Module Fragment

Import legacy headers:

module;  // Global module fragment

#include <vector>
#include <string>

export module mymodule;

export class MyClass {
    std::vector<std::string> data_;  // Uses std types
};

Private Module Fragment

Hide implementation details completely:

export module widget;

export class Widget {
public:
    void process();
};

module :private;  // Private fragment

// Implementation hidden from importers
void Widget::process() {
    // ...
}

// Helper functions not visible
void helper() {
    // ...
}

Advantages Over Headers

1. Faster Compilation

// With headers: Reparse every #include in every TU
#include <vector>  // Parsed in every .cpp file

// With modules: Parse once, import everywhere
import std;  // Pre-compiled module

2. No Macro Pollution

// Header leaks macros
#define MAX(a,b) ((a)>(b)?(a):(b))

// Module doesn't leak implementation details
export module math;
// Macros don't leak to importers

3. Order Independence

// Headers: Order matters
#include "b.h"  // Must come before a.h sometimes
#include "a.h"

// Modules: Order doesn't matter
import a;
import b;

4. Better Encapsulation

// Module interface
export module database;

export class Connection {
    // Only public interface exported
};

// Private implementation not exposed

Importing Standard Library

import std;  // Import entire standard library (C++23)

import std.core;  // Core utilities
import std.io;    // I/O facilities
import std.regex; // Regular expressions

Module vs Header Comparison

AspectHeadersModules
ParsingEvery TUOnce
MacrosLeakDon’t leak
OrderMattersIndependent
ODR ViolationsPossiblePrevented
Compile TimeSlowFast

Migration Strategy

Gradual adoption:

// 1. Start with new code
export module new_feature;

// 2. Wrap existing headers
export module legacy_wrapper;
export {
    #include "old_header.h"
}

// 3. Eventually refactor to pure modules

Best Practices

  1. One module per library: Don’t create too many small modules
  2. Use partitions for large modules
  3. Export minimal interface: Keep internals private
  4. Avoid global module fragment when possible
  5. Prefer module imports over header includes

Compilation Model

# Compile module interface (generates BMI - Binary Module Interface)
g++ -std=c++20 -c math.cppm -o math.o

# Compile code that imports module
g++ -std=c++20 -c main.cpp -o main.o

# Link
g++ math.o main.o -o program
Conceptual Check

What is the primary advantage of modules over headers?

Interactive Lab

Module Export

 module math;

export int multiply(int a, int b) {
    return a * b;
}