Modules (C++20) — concept and syntax
WHY do modules exist?
The student-friendly contrast:
#include (old) |
import (C++20) |
|---|---|
| Textual copy-paste | Compiled binary interface |
| Re-parsed every time | Parsed once |
| Macros leak in | Macros do not leak |
| Needs include guards | No guards needed |
| Order-sensitive | Order-independent |
WHAT is a module, precisely?
HOW do you write one? (syntax, built from scratch)
We will derive the structure piece by piece.
Step 1 — name the module and export it.
// math.ixx (primary interface unit)
export module math; // declares THIS file as module "math"Why this step? export module math; says "everything compiled here belongs to a unit called math; importers will use import math;." It must be the first real declaration.
Step 2 — export the things you want public.
export module math;
export int add(int a, int b) { // 'export' => visible to importers
return a + b;
}
int secret_helper() { return 42; } // NO export => private to the moduleWhy this step? The export keyword is the gate. add crosses into the importer's view; secret_helper stays inside. This is encapsulation at the file level.
Step 3 — export a whole group at once.
export { // export block: everything inside is exported
int sub(int a, int b) { return a - b; }
int mul(int a, int b) { return a * b; }
}Why this step? Saves repeating export. Pure convenience, identical meaning.
Step 4 — use it in another file.
// main.cpp
import math; // pull in the EXPORTED names of module math
#include <iostream>
int main() {
std::cout << add(2, 3); // works: add was exported
// secret_helper(); // ERROR: not exported, invisible here
}Why this step? import math; brings only the exported surface. Nothing else leaks — that's the whole point.
Step 5 — if you must use old headers, isolate them.
module; // start global module fragment
#include <cmath> // legacy header allowed ONLY here
export module geometry;
export double hypot2(double a, double b) {
return std::sqrt(a*a + b*b);
}Why this step? #include cannot appear after export module ...;. The global module fragment is the quarantine zone for preprocessor code.

Importing the Standard Library
Worked example: splitting interface and implementation
Common mistakes (Steel-manned)
Active recall
Recall Quick self-test (answer before expanding)
- What keyword line marks the primary interface unit? →
export module Name; - Where is the only legal place for
#includeinside a module? → the global module fragment. - Does
importleak macros? → No (header units excepted). - How many primary interface units per module? → Exactly one.
What does export module math; declare?
math; its exported names become importable.What is the global module fragment and where does it go?
module; and export module Name;; it is the only place legacy #include directives are allowed.Difference between export module m; and module m;?
export module m; is the (single) interface unit; module m; is an implementation unit that adds no new public names.Does importing a module bring its macros into your file?
Why are module builds faster than #include?
What keyword exports a single declaration?
export placed before the declaration (or wrapping several in an export { ... } block).How many primary interface units can a module have?
export module m:part;.Is a private member of an exported class visible to importers?
export crosses the module boundary, but private still blocks class-member access.Recall Feynman: explain it to a 12-year-old
Imagine your old way of sharing a recipe was photocopying the entire cookbook and stuffing it into every friend's bag — slow, and stray notes scrawled in the margins (macros) get copied too. A module is instead a sealed lunchbox: you decide exactly which dishes (export) you put on the lid for friends to take, and you keep the secret ingredients inside. Friends just say "give me the math lunchbox" (import math;) and get only the labeled dishes — no margin scribbles, and you made the box only once.
Connections
- Header Files and #include — the legacy mechanism modules replace.
- The C++ Preprocessor — why textual paste and macro leakage are problematic.
- Translation Units and Linkage — modules redefine the unit of compilation.
- Namespaces in C++ — both manage names, but namespaces don't control cross-file visibility.
- Build Systems and Compilation Speed — modules' main practical payoff.
- Module Partitions — splitting one module across many interface files.
Concept Map
Hinglish (regional understanding)
Intuition Hinglish mein samjho
Dekho yaar, purana #include system basically copy-paste hai — preprocessor poore header file ka text utha ke tumhari .cpp mein chipka deta hai. Agar ek header 100 files mein include hua, toh wo 100 baar parse hota hai (slow build), aur header ke andar ke #define macros chupke se tumhari file ko ganda kar dete hain. Modules isi problem ka clean solution hai: ek module ek baar compile hota hai binary interface mein, aur baaki files sirf import math; bolke use kar leti hain. No re-parsing, no macro leakage, no include guards.
Syntax ka core simple hai. Jis file mein tum public face define karte ho usme likho export module math; — ye "primary interface unit" hai, aur poore module mein sirf ek hi hota hai. Jo cheez bahar dikhani hai uske aage export lagao; baaki sab by default private rehta hai (encapsulation at file level). Agar purane headers chahiye (#include <cmath> type), toh unhe sirf "global module fragment" mein daalo — yani module; aur export module …; ke beech. Iske baad #include allowed nahi.
Do common galtiyaan yaad rakho. Pehli: log export ko class ke public jaisa samajh lete hain — galat. export decide karta hai ki kaunse naam file/module boundary paar karenge; public/private decide karta hai class ke andar member access. Dono alag axis hain. Doosri: log sochte hain import macros bhi laata hai — nahi laata, aur yahi to fayda hai. Agar macro chahiye hi to header unit (import "config.h";) use karo.
Practically iska sabse bada benefit build speed aur clean code organization hai. Bade projects mein modules compile time ko dramatically kam karte hain, aur interface (.ixx) vs implementation (module name;) ka separation tumhare code ko maintainable banata hai. Ek line yaad rakho order ke liye: module; → #includes → export module name; → exports.