Module C++20
Aflați de ce modulele înlocuiesc anteturile, cum să declarați și să exportați module, partițiile modulelor și calea de tranziție de la codul bazat pe antet.
De ce modulele înlocuiesc anteturile
The#includesystem has been the foundation of C++ compilation for decades, but it carries serious problems. Headers are textually included — the preprocessor copies the entire file into each translation unit. This means#include might paste 50,000+ lines of code into every.cppfile. The result: slow compilation, fragile include ordering (macros from one header affecting another), and One Definition Rule (ODR) violations that cause silent undefined behavior.
C++20 modules solve all of these problems. A module is compiled once into a binary interface, then imported by consumers without re-parsing. There is no textual inclusion, no macro leakage, and no include-order dependency. Build times can improve by 5-20x for large projects.
Module Declaration and Export
A module has an interface unit (declaring what's exported) and optionally one or more implementation units. Theexportkeyword makes declarations visible to importers:
// math_utils.cppm — module interface unit
export module math_utils; // module declaration
import <cmath>; // import a header unit
// Exported — visible to importers
export double circle_area(double radius) {
return std::numbers::pi * radius * radius;
}
export struct Point {
double x, y;
double distance_to(const Point& other) const {
double dx = x - other.x;
double dy = y - other.y;
return std::sqrt(dx * dx + dy * dy);
}
};
// NOT exported — module-private (internal linkage)
double helper_function(double val) {
return val * val;
}
// Export a namespace — all members become visible
export namespace geometry {
double triangle_area(double base, double height) {
return 0.5 * base * height;
}
double rectangle_area(double width, double height) {
return width * height;
}
}Importing Modules
Consumers useimportto access a module's exported declarations. Unlike#include,importdoes not perform textual inclusion — macros and implementation details do not leak across module boundaries:
// main.cpp — imports the module
import math_utils;
#include <iostream> // can still use #include for non-modularized headers
int main() {
double area = circle_area(5.0);
std::cout << "Circle area: " << area << "\n";
Point p1{0.0, 0.0};
Point p2{3.0, 4.0};
std::cout << "Distance: " << p1.distance_to(p2) << "\n"; // 5.0
std::cout << "Triangle: " << geometry::triangle_area(10, 5) << "\n";
// helper_function(42); // ERROR: not exported, not visible
return 0;
}
// Compile order matters for modules:
// 1. Compile the module interface first:
// g++ -std=c++20 -fmodules-ts -c math_utils.cppm
// 2. Then compile the consumer:
// g++ -std=c++20 -fmodules-ts main.cpp -o mainModule Partitions
Large modules can be split into partitions — sub-modules that together form the complete module interface. Partitions are internal to the module and cannot be imported by external consumers directly:
// --- math_utils-algebra.cppm (partition interface) ---
export module math_utils:algebra;
export double square(double x) { return x * x; }
export double cube(double x) { return x * x * x; }
// --- math_utils-trig.cppm (partition interface) ---
export module math_utils:trig;
import <cmath>;
export double deg_to_rad(double degrees) {
return degrees * std::numbers::pi / 180.0;
}
export double sin_deg(double degrees) {
return std::sin(deg_to_rad(degrees));
}
// --- math_utils.cppm (primary module interface) ---
export module math_utils;
// Re-export partitions — consumers see everything
export import :algebra;
export import :trig;
// Additional exports can go here
export double hypotenuse(double a, double b) {
return std::sqrt(square(a) + square(b));
}
// --- Consumer ---
// import math_utils; // gets algebra, trig, and hypotenuseimport std (C++23)
C++23 introduceimport std;care importă întreaga bibliotecă standard ca modul. Acesta este un schimbător de joc pentru viteza de compilare:
import std; // Everything: iostream, vector, string, algorithm, ...
int main() {
std::println("Hello from C++23 modules!");
std::vector<int> v{1, 2, 3};
std::ranges::sort(v);
}import std; replaces dozens of #include directives with a single import that compiles dramatically faster because the standard library module is pre-compiled. There is also import std.compat; which additionally provides C library names in the global namespace (like printf, strlen).
Compiler support is still maturing: As of 2024, GCC, Clang, and MSVC all support modules but with varying levels of completeness and different build system integration. MSVC has the most mature support.
Build system integration: CMake 3.28+ supports modules with theCXX_MODULE_SETSfeature, but the ecosystem (dependency scanners, package managers) is still catching up. Build order now matters — module interfaces must be compiled before their consumers.
No macro export: Modules deliberately do not export#definemacros. Code that relies on macros from headers (likeWIN32_LEAN_AND_MEANorNOMINMAX) must handle this differently.
Migration strategy: Start withimport std;(C++23) to replace standard library includes, then gradually modularize your own code starting with leaf libraries that have few dependencies.
La adoptarea modulelor:
1. Păstrați interfețele modulelor slabe — exportați doar ceea ce au nevoie consumatorii. Entitățile neexportate au legătură cu module și nu pot cauza încălcări ODR.
2. Utilizați partiții pentru module mari — împărțit în sub-module logice (de exemplu,:types,:algorithms,:io).
3. Preferexport namespace — exportați spații de nume întregi, mai degrabă decât declarații individuale, pentru a menține interfața organizată.
4. Nu amestecați#includeșiimportpentru aceeași bibliotecă - alegeți una și fiți consecvenți într-o unitate de traducere.
5. Utilizați extensiile.cppmsau.ixx pentru fișierele de interfață a modulului, astfel încât sistemele de construcție să le poată identifica automat.
- Modulele înlocuiesc
#includecuimport— fără includere de text, fără scurgeri de macro, fără erori de includere a comenzii - Utilizați
exportpentru a face declarațiile vizibile pentru importatori; entitățile neexportate sunt module private - Partițiile de module împart modulele mari în subcomponente, prezentând în același timp o interfață unificată
import std;(C++23) înlocuiește toate bibliotecile standard incluse cu un singur import rapid- Sistemele de construcție trebuie să compilați interfețele modulelor înaintea consumatorilor — ordinea de compilare contează
Test — Verifică-ți cunoștințele
(15 XP)1. Care este avantajul principal al modulelor față de #include?
2. Ce se întâmplă cu o funcție definită într-un modul, dar NU marcată cu`export`?
3. What does`import std;`in C++23 do?