Namensräume
Varianten
Aktionen

Module (seit C++20)

Von cppreference.com
< cpp‎ | Sprache
 
 
C++ Sprache
Allgemeine Themen
Kontrollfluss
Bedingte Ausführungsaussagen
if
Iterationsanweisungen (Schleifen)
for
Bereichs-for (C++11)
Sprunganweisungen
Funktionen
Funktionsdeklaration
Lambda-Funktionsausdruck
inline-Spezifizierer
Dynamische Ausnahmespezifikationen (bis C++17*)
noexcept-Spezifizierer (C++11)
Ausnahmen
Namensräume
Typen
Spezifizierer
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Speicherdauer-Spezifizierer
Initialisierung
Ausdrücke
Alternative Darstellungen
Literale
Boolesch - Ganzzahl - Gleitkommazahl
Zeichen - String - nullptr (C++11)
Benutzerdefinierte (C++11)
Dienstprogramme
Attribute (C++11)
Typen
typedef-Deklaration
Typalias-Deklaration (C++11)
Umwandlungen
Speicherzuweisung
Klassen
Klassenspezifische Funktionseigenschaften
explicit (C++11)
static

Spezielle Member-Funktionen
Templates
Sonstiges
 
 

Die meisten C++-Projekte verwenden mehrere Übersetzungseinheiten und müssen daher Deklarationen und Definitionen über diese Einheiten hinweg teilen. Die Verwendung von Headern ist für diesen Zweck prominent, ein Beispiel ist die Standardbibliothek, deren Deklarationen durch Einbinden des entsprechenden Headers bereitgestellt werden können.

Module sind ein Sprachmerkmal zum Teilen von Deklarationen und Definitionen über Übersetzungseinheiten hinweg. Sie sind eine Alternative zu einigen Anwendungsfällen von Headern.

Module sind orthogonal zu Namespaces.

// helloworld.cpp
export module helloworld; // module declaration
 
import <iostream>;        // import declaration
 
export void hello()       // export declaration
{
    std::cout << "Hello world!\n";
}
// main.cpp
import helloworld; // import declaration
 
int main()
{
    hello();
}

Inhalt

[bearbeiten] Syntax

export(optional) module modulname modulpartition (optional) attr (optional) ; (1)
export deklaration (2)
export { deklarationssequenz (optional) } (3)
export(optional) import modulname attr (optional) ; (4)
export(optional) import modulpartition attr (optional) ; (5)
export(optional) import headername attr (optional) ; (6)
module; (7)
module : private; (8)
1) Moduldeklaration. Deklariert, dass die aktuelle Übersetzungseinheit eine Moduleinheit ist.
2,3) Exportdeklaration. Exportiert alle Deklarationen im Namespace-Gültigkeitsbereich in deklaration oder deklarationssequenz.
4,5,6) Importdeklaration. Importiert eine Moduleinheit/Modulpartition/Header-Einheit.
7) Beginnt ein globales Modulfragment.
8) Beginnt ein privates Modulfragment.

[bearbeiten] Moduldeklarationen

Eine Übersetzungseinheit kann eine Moduldeklaration haben, in diesem Fall gilt sie als Moduleinheit. Die Moduldeklaration muss, falls vorhanden, die erste Deklaration der Übersetzungseinheit sein (außer dem globalen Modulfragment, das später behandelt wird). Jede Moduleinheit ist einem Modulnamen (und optional einer Partition) zugeordnet, der in der Moduldeklaration angegeben ist.

export(optional) module modulname modulpartition (optional) attr (optional) ;

Der Modulname besteht aus einem oder mehreren durch Punkte getrennten Bezeichnern (z. B.: mymodule, mymodule.mysubmodule, mymodule2...). Punkte haben keine intrinsische Bedeutung, werden aber informell zur Darstellung von Hierarchien verwendet.

Wenn ein Bezeichner im Modulnamen oder in der Modulpartition als objektartiges Makro definiert ist, ist das Programm ill-formed.

Ein benanntes Modul ist die Sammlung von Moduleinheiten mit demselben Modulnamen.

Moduleinheiten, deren Deklaration das Schlüsselwort export enthält, werden als Modulschnittstelleneinheiten bezeichnet; alle anderen Moduleinheiten werden als Modulimplementierungseinheiten bezeichnet.

Für jedes benannte Modul muss es genau eine Modulschnittstelleneinheit geben, die keine Modulpartition angibt; diese Moduleinheit wird als primäre Modulschnittstelleneinheit bezeichnet. Ihr exportierter Inhalt wird beim Importieren des entsprechenden benannten Moduls verfügbar sein.

// (each line represents a separate translation unit)
 
export module A;   // declares the primary module interface unit for named module 'A'
module A;          // declares a module implementation unit for named module 'A'
module A;          // declares another module implementation unit for named module 'A'
export module A.B; // declares the primary module interface unit for named module 'A.B'
module A.B;        // declares a module implementation unit for named module 'A.B'

[bearbeiten] Exportieren von Deklarationen und Definitionen

Modulschnittstelleneinheiten können Deklarationen (einschließlich Definitionen) exportieren, die von anderen Übersetzungseinheiten importiert werden können. Um eine Deklaration zu exportieren, stellen Sie ihr entweder das Schlüsselwort export voran oder platzieren Sie sie innerhalb eines export-Blocks.

export deklaration
export { deklarationssequenz (optional) }
export module A; // declares the primary module interface unit for named module 'A'
 
// hello() will be visible by translations units importing 'A'
export char const* hello() { return "hello"; } 
 
// world() will NOT be visible.
char const* world() { return "world"; }
 
// Both one() and zero() will be visible.
export
{
    int one()  { return 1; }
    int zero() { return 0; }
}
 
// Exporting namespaces also works: hi::english() and hi::french() will be visible.
export namespace hi
{
    char const* english() { return "Hi!"; }
    char const* french()  { return "Salut!"; }
}

[bearbeiten] Importieren von Modulen und Header-Einheiten

Module werden über eine Importdeklaration importiert.

export(optional) import modulname attr (optional) ;

Alle Deklarationen und Definitionen, die in den Modulschnittstelleneinheiten des gegebenen benannten Moduls exportiert werden, sind in der Übersetzungseinheit, die die Importdeklaration verwendet, verfügbar.

Importdeklarationen können in einer Modulschnittstelleneinheit exportiert werden. Das heißt, wenn Modul B A export-importiert, dann macht das Importieren von B auch alle Exporte von A sichtbar.

In Moduleinheiten müssen alle Importdeklarationen (einschließlich Export-Importe) nach der Moduldeklaration und vor allen anderen Deklarationen gruppiert werden.

/////// A.cpp (primary module interface unit of 'A')
export module A;
 
export char const* hello() { return "hello"; }
 
/////// B.cpp (primary module interface unit of 'B')
export module B;
 
export import A;
 
export char const* world() { return "world"; }
 
/////// main.cpp (not a module unit)
#include <iostream>
import B;
 
int main()
{
    std::cout << hello() << ' ' << world() << '\n';
}

#include sollte nicht in einer Moduleinheit verwendet werden (außerhalb des globalen Modulfragments), da alle inkludierten Deklarationen und Definitionen als Teil des Moduls betrachtet würden. Stattdessen können Header auch als Header-Einheiten mit einer Importdeklaration importiert werden.

export(optional) import headername attr (optional) ;

Eine Header-Einheit ist eine separate Übersetzungseinheit, die aus einem Header synthetisiert wird. Das Importieren einer Header-Einheit macht alle ihre Definitionen und Deklarationen zugänglich. Präprozessor-Makros sind ebenfalls zugänglich (da Importdeklarationen vom Präprozessor erkannt werden).

Im Gegensatz zu #include beeinflussen jedoch bereits am Punkt der Importdeklaration definierte Präprozessor-Makros nicht die Verarbeitung des Headers. Dies kann in einigen Fällen unbequem sein (einige Header verwenden Präprozessor-Makros als eine Form der Konfiguration), in diesem Fall ist die Verwendung des globalen Modulfragments erforderlich.

/////// A.cpp (primary module interface unit of 'A')
export module A;
 
import <iostream>;
export import <string_view>;
 
export void print(std::string_view message)
{
    std::cout << message << std::endl;
}
 
/////// main.cpp (not a module unit)
import A;
 
int main()
{
    std::string_view message = "Hello, world!";
    print(message);
}

[bearbeiten] Globales Modulfragment

Moduleinheiten können mit einem globalen Modulfragment präfixiert werden, das verwendet werden kann, um Header einzubinden, wenn das Importieren der Header nicht möglich ist (insbesondere wenn der Header Präprozessor-Makros als Konfiguration verwendet).

module;

präprozessor-direktiven (optional)

modul-deklaration

Wenn eine Moduleinheit ein globales Modulfragment hat, muss ihre erste Deklaration module; lauten. Dann können nur Präprozessor-Direktiven im globalen Modulfragment erscheinen. Dann kennzeichnet eine Standard-Moduldeklaration das Ende des globalen Modulfragments und den Beginn des Modulinhalts.

/////// A.cpp (primary module interface unit of 'A')
module;
 
// Defining _POSIX_C_SOURCE adds functions to standard headers,
// according to the POSIX standard.
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
 
export module A;
 
import <ctime>;
 
// Only for demonstration (bad source of randomness).
// Use C++ <random> instead.
export double weak_random()
{
    std::timespec ts;
    std::timespec_get(&ts, TIME_UTC); // from <ctime>
 
    // Provided in <stdlib.h> according to the POSIX standard.
    srand48(ts.tv_nsec);
 
    // drand48() returns a random number between 0 and 1.
    return drand48();
}
 
/////// main.cpp (not a module unit)
import <iostream>;
import A;
 
int main()
{
    std::cout << "Random value between 0 and 1: " << weak_random() << '\n';
}

[bearbeiten] Privates Modulfragment

Die primäre Modulschnittstelleneinheit kann mit einem privaten Modulfragment suffigiert werden, das es ermöglicht, ein Modul als einzelne Übersetzungseinheit darzustellen, ohne den gesamten Inhalt des Moduls für Importierende erreichbar zu machen.

module : private;

deklarationssequenz (optional)

Privates Modulfragment beendet den Teil der Modulschnittstelleneinheit, der das Verhalten anderer Übersetzungseinheiten beeinflussen kann. Wenn eine Moduleinheit ein privates Modulfragment enthält, ist sie die einzige Moduleinheit ihres Moduls.

export module foo;
 
export int f();
 
module : private; // ends the portion of the module interface unit that
                  // can affect the behavior of other translation units
                  // starts a private module fragment
 
int f()           // definition not reachable from importers of foo
{
    return 42;
}

[bearbeiten] Modulpartitionen

Ein Modul kann Modulpartitions-Einheiten haben. Dies sind Moduleinheiten, deren Moduldeklarationen eine Modulpartition enthalten, die mit einem Doppelpunkt : beginnt und nach dem Modulnamen platziert wird.

export module A:B; // Declares a module interface unit for module 'A', partition ':B'.

Eine Modulpartition repräsentiert genau eine Moduleinheit (zwei Moduleinheiten können nicht dieselbe Modulpartition bezeichnen). Sie sind nur innerhalb des benannten Moduls sichtbar (Übersetzungseinheiten außerhalb des benannten Moduls können eine Modulpartition nicht direkt importieren).

Eine Modulpartition kann von Moduleinheiten desselben benannten Moduls importiert werden.

export(optional) import modulpartition attr (optional) ;
/////// A-B.cpp   
export module A:B;
...
 
/////// A-C.cpp
module A:C;
...
 
/////// A.cpp
export module A;
 
import :C;
export import :B;
 
...

Alle Definitionen und Deklarationen in einer Modulpartition sind für die importierende Moduleinheit sichtbar, unabhängig davon, ob sie exportiert wurden oder nicht.

Modulpartitionen können Modulschnittstelleneinheiten sein (wenn ihre Moduldeklarationen export haben). Sie müssen von der primären Modulschnittstelleneinheit export-importiert werden, und ihre exportierten Anweisungen werden sichtbar, wenn das Modul importiert wird.

export(optional) import modulpartition attr (optional) ;
///////  A.cpp   
export module A;     // primary module interface unit
 
export import :B;    // Hello() is visible when importing 'A'.
import :C;           // WorldImpl() is now visible only for 'A.cpp'.
// export import :C; // ERROR: Cannot export a module implementation unit.
 
// World() is visible by any translation unit importing 'A'.
export char const* World()
{
    return WorldImpl();
}
/////// A-B.cpp 
export module A:B; // partition module interface unit
 
// Hello() is visible by any translation unit importing 'A'.
export char const* Hello() { return "Hello"; }
/////// A-C.cpp 
module A:C; // partition module implementation unit
 
// WorldImpl() is visible by any module unit of 'A' importing ':C'.
char const* WorldImpl() { return "World"; }
/////// main.cpp 
import A;
import <iostream>;
 
int main()
{
    std::cout << Hello() << ' ' << World() << '\n';
    // WorldImpl(); // ERROR: WorldImpl() is not visible.
}

[bearbeiten] Modulbesitz

Im Allgemeinen gilt: Wenn eine Deklaration nach der Moduldeklaration in einer Moduleinheit erscheint, ist sie diesem Modul zugeordnet.

Wenn eine Deklaration einer Entität einem benannten Modul zugeordnet ist, kann diese Entität nur in diesem Modul definiert werden. Alle Deklarationen einer solchen Entität müssen demselben Modul zugeordnet sein.

Wenn eine Deklaration einem benannten Modul zugeordnet ist und nicht exportiert wird, hat der deklarierte Name eine Modulbindung.

export module lib_A;
 
int f() { return 0; } // f has module linkage
export int x = f();   // x equals 0
export module lib_B;
 
int f() { return 1; } // OK, f in lib_A and f in lib_B refer to different entities
export int y = f(); // y equals 1

Wenn zwei Deklarationen einer Entität unterschiedlichen Modulen zugeordnet sind, ist das Programm ill-formed; keine Diagnose ist erforderlich, wenn keine der beiden von der anderen erreichbar ist.

/////// decls.h
int f(); // #1, attached to the global module
int g(); // #2, attached to the global module
/////// Module interface of M
module;
#include "decls.h"
export module M;
export using ::f; // OK, does not declare an entity, exports #1
int g();          // Error: matches #2, but attached to M
export int h();   // #3
export int k();   // #4
/////// Other translation unit
import M;
static int h();   // Error: matches #3
int k();          // Error: matches #4

Die folgenden Deklarationen sind keinem benannten Modul zugeordnet (und somit kann die deklarierte Entität außerhalb des Moduls definiert werden)

export module lib_A;
 
namespace ns // ns is not attached to lib_A.
{
    export extern "C++" int f(); // f is not attached to lib_A.
           extern "C++" int g(); // g is not attached to lib_A.
    export              int h(); // h is attached to lib_A.
}
// ns::h must be defined in lib_A, but ns::f and ns::g can be defined elsewhere (e.g.
// in a traditional source file).

[bearbeiten] Anmerkungen

Feature-Test-Makro Wert Std Feature
__cpp_modules 201907L (C++20) Module — Kernsprache-Unterstützung
__cpp_lib_modules 202207L (C++23) Standardbibliotheksmodule std und std.compat

[bearbeiten] Schlüsselwörter

private, module, import, export

[bearbeiten] Fehlerberichte

Die folgenden Verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.

DR angewendet auf Verhalten wie veröffentlicht Korrigiertes Verhalten
CWG 2732 C++20 war unklar, ob importierbare Header
auf den Präprozessorzustand vom Importpunkt aus reagieren können
keine Reaktion
P3034R1 C++20 Modulnamen und Modulpartitionen könnten
Bezeichner enthalten, die als objektartige Makros definiert sind
verboten