Motivation
C++20 modules exist to replace the traditional header-based inclusion model with a safer and more efficient system. The old model relied on textual inclusion, which caused long compile times, complicated dependency structures, and issues such as accidental name collisions and macro interference.
Old method: header and implementation
math_utils.h
#ifndef MATH_UTILS_H
#define MATH_UTILS_H
int add(int a, int b);
#endif
math_utils.cpp
#include "math_utils.h"
int add(int a, int b) {
return a + b;
}
main.cpp
#include "math_utils.h"
#include <iostream>
int main() {
std::cout << add(2, 3) << "\n";
}
New method: module interface and import
math_utils.ixx (module interface)
export module math_utils;
export int add(int a, int b);
math_utils.cpp (module implementation)
module math_utils;
int add(int a, int b) {
return a + b;
}
main.cpp
import math_utils;
#include <iostream>
int main() {
std::cout << add(2, 3) << "\n";
}
What's the difference?
The module-based version improves the build process because the compiler does not need to repeatedly re-parse the same header text in every translation unit. Instead, the module interface is compiled once into a binary module interface, and all importing files can reuse that precompiled result.
Modules also avoid problems caused by the preprocessor. Textual inclusion allows macros and accidental name clashes to leak into or out of headers, which can cause subtle bugs. Modules create a clear boundary: only the names explicitly exported become visible, and macros cannot cross that boundary.
This results in faster builds, cleaner code organization, and more reliable separation between interface and implementation.