C++ 분할 컴파일: 헤더 #include 비용과 C++20 모듈 차이 (Tour 3.2)
Short answer: In C++ separate compilation, traditional #include headers cause repeated parsing and dependency leaks, while C++20 Modules solve these overheads through single-time compilation and macro isolation.
This post follows the A Tour of C++ 3.2 Separate Compilation study notes. It lightly assumes the 3.1 idea of separating declaration from definition without re-quizzing that material, and focuses only on separate compilation itself.
Why does #including a header make compilation more expensive?
Short answer: Every time a header is #included from multiple source files, its text is copied in and parsed again, which sharply increases compile time.
A header file (.h) collects interface declarations, and any source file that needs them pulls them in with #include. Because #include is just macro-based text insertion, when several source files include the same header, that header’s contents are copied in and reparsed from scratch every time. As the number of source files grows, so does the cost of reparsing the same declarations over and over.
Two more problems come with this. One is order dependency: depending on the #include order, the meaning of macros or declarations can be distorted or conflict. The other is transitivity: if a header itself includes other headers, unnecessary declarations and macros from those inner headers leak through to whatever includes the outer header.

cost of repeatedly parsing #include headers
How are C++20 modules different from headers?
Short answer: A module is compiled exactly once as its own unit, blocks macro pollution, and is non-transitive, so import order does not affect its meaning.
A module is compiled once as a separate file, then brought in with the import keyword. Since it is not reparsed for every translation unit, compilation speed improves.
Modules address each of the header’s three problems. First, they are non-transitive: anything a module imports internally does not automatically leak to code that imports that module. Second, they are order-independent: changing the order of import statements does not create meaning conflicts. Third, they isolate macros: macros defined inside a module are not exported, which prevents name pollution.

modules vs headers
How do the header and module code structures compare?
Short answer: A header keeps declarations in a .h file that source files pull in via #include, while a module uses the export module and import keywords to expose only the identifiers it explicitly marks.
Placing the study notes’ two examples side by side:
// Vector.h
#pragma once
class Vector {
public:
Vector(int s);
};
// Vector.cpp
#include "Vector.h"
Vector::Vector(int s) { /* ... */ }
// user.cpp
#include "Vector.h"
#include <iostream>
int main() { /* ... */ }
// Vector.ixx / Vector.cppm
export module Vector;
export class Vector {
public:
Vector(int s);
};
export bool operator==(const Vector& v1, const Vector& v2) { /* ... */ }
// user.cpp
import Vector;
import std;
int main() { /* ... */ }
In the header approach, both Vector.cpp and user.cpp pull in the declarations as text via #include "Vector.h". In the module approach, export module Vector; defines the module and only declarations marked export are made public, while the consuming code uses import Vector; to bring in just the identifiers it needs.
FAQ
Q. What does a module’s “non-transitive” property mean?
A. It means that another module imported inside a given module does not automatically leak to external code that imports that module, which prevents unnecessary dependency propagation.
Q. Does the introduction of C++20 modules mean headers are no longer used?
A. No. The module system is fully compatible with existing #include-based code, so existing projects can keep working as-is while gradually adopting modules for new code.
Sources
C++ study notes — 3.2 Separate Compilation