Partager via


Didacticiel sur les modules nommés (C++)

Ce tutoriel concerne la création de modules C++20. Les modules remplacent les fichiers d’en-tête. Vous découvrirez comment les modules constituent une amélioration des fichiers d’en-tête.

Dans ce didacticiel, découvrez comment :

  • Créer et importer un module
  • Créer une unité d’interface de module principal
  • Créer un fichier de partition de module
  • Créer un fichier d’implémentation d’unité de module

Prérequis

Ce tutoriel nécessite Visual Studio 2022 17.1.0 ou version ultérieure.

Vous pouvez obtenir des erreurs IntelliSense lors de l’utilisation de l’exemple de code dans ce tutoriel. Le travail sur le moteur IntelliSense rattrape le compilateur. Les erreurs IntelliSense peuvent être ignorées et n’empêchent pas la génération de l’exemple de code. Pour suivre la progression du travail IntelliSense, consultez ce problème.

Qu’est-ce que les modules C++

Les fichiers d’en-tête sont la façon dont les déclarations et les définitions sont partagées entre les fichiers sources en C++. Les fichiers d’en-tête sont fragiles et difficiles à composer. Elles peuvent être compilées différemment selon l’ordre dans lequel vous les incluez, ou sur les macros qui sont ou ne sont pas définies. Ils peuvent ralentir le temps de compilation, car ils sont traités pour chaque fichier source qui les inclut.

C++20 introduit une approche moderne de la composante des programmes C++ : modules.

Comme les fichiers d’en-tête, les modules vous permettent de partager des déclarations et des définitions entre les fichiers sources. Mais contrairement aux fichiers d’en-tête, les modules ne fuitent pas de définitions de macros ni de détails d’implémentation privée.

Les modules sont plus faciles à composer, car leur sémantique ne change pas en raison de définitions de macros ou de ce qui a été importé, de l’ordre des importations, etc. Ils facilitent également le contrôle de ce qui est visible par les consommateurs.

Les modules fournissent des garanties de sécurité supplémentaires que les fichiers d’en-tête ne le font pas. Le compilateur et l’éditeur de liens fonctionnent ensemble pour éviter les éventuels problèmes de collision de noms et fournir des garanties plus fortes en matière de règle de définition (ODR).

Un modèle de propriété forte évite les conflits entre les noms au moment du lien, car l’éditeur de liens attache des noms exportés au module qui les exporte. Ce modèle permet au compilateur Microsoft Visual C++ d’empêcher le comportement non défini provoqué par la liaison de différents modules qui signalent des noms similaires dans le même programme. Pour plus d’informations, consultez Propriété forte.

Un module est constitué d’un ou plusieurs fichiers de code source compilés dans un fichier binaire. Le fichier binaire décrit tous les types, fonctions et modèles exportés dans le module. Lorsqu’un fichier source importe un module, le compilateur lit dans le fichier binaire qui contient le contenu du module. La lecture du fichier binaire est beaucoup plus rapide que le traitement d’un fichier d’en-tête. En outre, le fichier binaire est réutilisé par le compilateur chaque fois que le module est importé, ce qui permet d’économiser encore plus de temps. Étant donné qu’un module est généré une fois plutôt que chaque fois qu’il est importé, le temps de génération peut être réduit, parfois considérablement.

Plus important encore, les modules n’ont pas les problèmes de fragilité que font les fichiers d’en-tête. L’importation d’un module ne modifie pas la sémantique du module ou la sémantique d’un autre module importé. Les macros, les directives de préprocesseur et les noms non exportés déclarés dans un module ne sont pas visibles par le fichier source qui l’importe. Vous pouvez importer des modules dans n’importe quel ordre et ne modifiera pas la signification des modules.

Les modules peuvent être utilisés côte à côte avec les fichiers d’en-tête. Cette fonctionnalité est pratique si vous migrez une base de code pour utiliser des modules, car vous pouvez le faire en phases.

Dans certains cas, un fichier d’en-tête peut être importé en tant qu’unité d’en-tête plutôt qu’en tant que #include fichier. Les unités d’en-tête sont l’alternative recommandée aux fichiers d’en-tête précompilés (PCH). Ils sont plus faciles à configurer et à utiliser que les fichiers PCH partagés, mais ils offrent des avantages similaires aux performances. Pour plus d’informations, consultez Procédure pas à pas : Générer et importer des unités d’en-tête dans Microsoft Visual C++.

Votre code peut consommer des modules dans le même projet, ou tous les projets référencés, automatiquement à l’aide de références de projet à projet aux projets de bibliothèque statique.

Créer le projet

À mesure que nous créons un projet simple, nous allons examiner différents aspects des modules. Le projet implémente une API à l’aide d’un module au lieu d’un fichier d’en-tête.

Dans Visual Studio 2022 ou version ultérieure, choisissez Créer un projet , puis le type de projet Application console (pour C++). Si ce type de projet n’est pas disponible, vous n’avez peut-être pas sélectionné le développement Desktop avec la charge de travail C++ lorsque vous avez installé Visual Studio. Vous pouvez utiliser Visual Studio Installer pour ajouter la charge de travail C++.

Donnez au nouveau projet le nom ModulesTutorial et créez le projet.

Étant donné que les modules sont une fonctionnalité C++20, utilisez l’option ou /std:c++latest le /std:c++20 compilateur. Dans le Explorateur de solutions, cliquez avec le bouton droit sur le nom ModulesTutorialdu projet, puis choisissez Propriétés. Dans la boîte de dialogue Pages de propriétés du projet, remplacez Configuration par Toutes les configurations et plateforme par toutes les plateformes. Sélectionnez Propriétés>de configuration générales dans le volet d’arborescence à gauche. Sélectionnez la propriété C++ Language Standard . Utilisez la liste déroulante pour remplacer la valeur de propriété par ISO C++20 Standard (/std :c++20). Sélectionnez OK pour accepter la modification.

A screenshot of the ModulesTutorial property page with the left pane open to Configuration Properties > General, and the C++ Language Standard dropdown open with ISO C++20 Standard (/std:c++20) selected

Créer l’unité d’interface du module principal

Un module se compose d’un ou plusieurs fichiers. L’un de ces fichiers doit être ce qu’on appelle l’unité d’interface du module principal. Il définit ce que le module exporte ; c’est-à-dire ce que les importateurs du module verront. Il ne peut y avoir qu’une seule unité d’interface de module principal par module.

Pour ajouter une unité d’interface de module primaire, dans Explorateur de solutions, cliquez avec le bouton droit sur Fichiers sources, puis sélectionnez Ajouter>un module.

Add item dialog in solution explorer with Add > Module... highlighted to illustrate where to click to add a module.

Dans la boîte de dialogue Ajouter un nouvel élément qui s’affiche, donnez au nouveau module le nom BasicPlane.Figures.ixx et choisissez Ajouter.

Le contenu par défaut du fichier de module créé comporte deux lignes :

export module BasicPlane;

export void MyFunc();

Les export module mot clé dans la première ligne déclarent que ce fichier est une unité d’interface de module. Il existe un point subtil ici : pour chaque module nommé, il doit y avoir exactement une unité d’interface de module sans partition de module spécifiée. Cette unité de module est appelée unité d’interface de module principale.

L’unité d’interface du module principal est l’emplacement où vous déclarez les fonctions, types, modèles, autres modules et partitions de module à exposer lorsque les fichiers sources importent le module. Un module peut se composer de plusieurs fichiers, mais seul le fichier d’interface du module principal identifie ce qu’il faut exposer.

Remplacez le contenu du BasicPlane.Figures.ixx fichier par :

export module BasicPlane.Figures; // the export module keywords mark this file as a primary module interface unit

Cette ligne identifie ce fichier comme interface du module principal et donne au module un nom : BasicPlane.Figures. La période du nom du module n’a aucune signification particulière pour le compilateur. Une période peut être utilisée pour transmettre la façon dont votre module est organisé. Si vous avez plusieurs fichiers de module qui fonctionnent ensemble, vous pouvez utiliser des périodes pour indiquer une séparation des problèmes. Dans ce tutoriel, nous allons utiliser des périodes pour indiquer différentes zones fonctionnelles de l’API.

Ce nom est également l’endroit où provient le « nommé » dans le « module nommé ». Les fichiers qui font partie de ce module utilisent ce nom pour s’identifier dans le cadre du module nommé. Un module nommé est la collection d’unités de module portant le même nom de module.

Nous devrions parler de l’API que nous allons implémenter pour un moment avant d’aller plus loin. Cela a un impact sur les choix que nous faisons ensuite. L’API représente différentes formes. Nous allons uniquement fournir quelques formes dans cet exemple : Point et Rectangle. Point est destiné à être utilisé dans le cadre de formes plus complexes, telles que Rectangle.

Pour illustrer certaines fonctionnalités des modules, nous allons intégrer cette API en éléments. Un élément sera l’API Point . L’autre partie sera Rectangle. Imaginez que cette API s’agrandit en quelque chose de plus complexe. La division est utile pour séparer les préoccupations ou faciliter la maintenance du code.

Jusqu’à présent, nous avons créé l’interface du module principal qui expose cette API. Nous allons maintenant générer l’API Point . Nous voulons qu’elle fait partie de ce module. Pour des raisons d’organisation logique et d’efficacité de génération potentielle, nous voulons rendre cette partie de l’API facilement compréhensible par elle-même. Pour ce faire, nous allons créer un fichier de partition de module.

Un fichier de partition de module est un élément ou un composant d’un module. Ce qui le rend unique est qu’il peut être traité comme un élément individuel du module, mais uniquement dans le module. Les partitions de module ne peuvent pas être consommées en dehors d’un module. Les partitions de module sont utiles pour diviser l’implémentation du module en éléments gérables.

Lorsque vous importez une partition dans le module principal, toutes ses déclarations deviennent visibles pour le module principal, qu’elles soient exportées ou non. Les partitions peuvent être importées dans n’importe quelle interface de partition, interface du module principal ou unité de module qui appartient au module nommé.

Créer un fichier de partition de module

Point partition de module

Pour créer un fichier de partition de module, dans le Explorateur de solutions cliquez avec le bouton droit sur Fichiers sources, puis sélectionnez Ajouter>un module. Nommez le fichier BasicPlane.Figures-Point.ixx et choisissez Ajouter.

Étant donné qu’il s’agit d’un fichier de partition de module, nous avons ajouté un trait d’union et le nom de la partition au nom du module. Cette convention aide le compilateur dans le cas de ligne de commande, car le compilateur utilise des règles de recherche de noms basées .ifc sur le nom du module pour rechercher le fichier compilé pour la partition. De cette façon, vous n’avez pas besoin de fournir des arguments de ligne de commande explicites /reference pour rechercher les partitions qui appartiennent au module. Il est également utile d’organiser les fichiers qui appartiennent à un module par nom, car vous pouvez facilement voir quels fichiers appartiennent aux modules.

Remplacez le contenu du champ BasicPlane.Figures-Point.ixx avec :

export module BasicPlane.Figures:Point; // defines a module partition, Point, that's part of the module BasicPlane.Figures

export struct Point
{
    int x, y;
};

Si le fichier commence par export module. Ces mot clé sont également la façon dont l’interface du module principal commence. Ce qui rend ce fichier différent est le signe deux-points (:) suivant le nom du module, suivi du nom de la partition. Cette convention d’affectation de noms identifie le fichier en tant que partition de module. Étant donné qu’elle définit l’interface de module pour une partition, elle n’est pas considérée comme l’interface du module principal.

Le nom BasicPlane.Figures:Point identifie cette partition dans le cadre du module BasicPlane.Figures. (N’oubliez pas que la période du nom n’a aucune signification particulière pour le compilateur). Le signe deux-points indique que ce fichier contient une partition de module nommée Point qui appartient au module BasicPlane.Figures. Nous pouvons importer cette partition dans d’autres fichiers qui font partie de ce module nommé.

Dans ce fichier, le export mot clé rend struct Point visible les consommateurs.

Rectangle partition de module

La prochaine partition que nous allons définir est Rectangle. Créez un autre fichier de module en suivant les mêmes étapes que précédemment : dans Explorateur de solutions, cliquez avec le bouton droit sur Fichiers sources, puis sélectionnez Ajouter>un module. Nommez le fichier BasicPlane.Figures-Rectangle.ixx et sélectionnez Ajouter.

Remplacez le contenu du champ BasicPlane.Figures-Rectangle.ixx avec :

export module BasicPlane.Figures:Rectangle; // defines the module partition Rectangle

import :Point;

export struct Rectangle // make this struct visible to importers
{
    Point ul, lr;
};

// These functions are declared, but will
// be defined in a module implementation file
export int area(const Rectangle& r);
export int height(const Rectangle& r);
export int width(const Rectangle& r);

Le fichier commence par export module BasicPlane.Figures:Rectangle; lequel déclare une partition de module qui fait partie du module BasicPlane.Figures. L’ajout :Rectangle au nom du module le définit comme une partition du module BasicPlane.Figures. Il peut être importé individuellement dans l’un des fichiers de module qui font partie de ce module nommé.

Ensuite, import :Point; montre comment importer une partition de module. L’instruction import rend tous les types, fonctions et modèles exportés dans la partition de module visibles pour le module. Vous n’avez pas besoin de spécifier le nom du module. Le compilateur sait que ce fichier appartient au BasicPlane.Figures module en raison de la export module BasicPlane.Figures:Rectangle; partie supérieure du fichier.

Ensuite, le code exporte la définition et struct Rectangle les déclarations pour certaines fonctions qui retournent différentes propriétés du rectangle. L’mot clé export indique s’il faut rendre ce qu’il précède aux consommateurs du module. Il est utilisé pour rendre les fonctions area, heightet width visible en dehors du module.

Toutes les définitions et déclarations d’une partition de module sont visibles par l’unité de module d’importation, qu’elles aient la export mot clé ou non. L’mot clé export détermine si la définition, la déclaration ou le typedef sera visible en dehors du module lorsque vous exportez la partition dans l’interface du module principal.

Les noms sont rendus visibles par les consommateurs d’un module de plusieurs façons :

  • Placez le mot clé export devant chaque type, fonction, et ainsi de suite, que vous souhaitez exporter.
  • Si vous placez export devant un espace de noms, par exemple export namespace N { ... }, tout ce qui est défini dans les accolades est exporté. Mais si ailleurs dans le module que vous définissez namespace N { struct S {...};}, il struct S n’est pas disponible pour les consommateurs du module. Il n’est pas disponible, car cette déclaration d’espace de noms n’est pas précédée par export, même s’il existe un autre espace de noms portant le même nom que celui-ci.
  • Si un type, une fonction, et ainsi de suite, ne doit pas être exporté, omettez le export mot clé. Il sera visible par d’autres fichiers qui font partie du module, mais pas pour les importateurs du module.
  • Permet module :private; de marquer le début de la partition du module privé. La partition de module privé est une section du module où les déclarations sont visibles uniquement pour ce fichier. Ils ne sont pas visibles pour les fichiers qui importent ce module ou vers d’autres fichiers qui font partie de ce module. Considérez-le comme une section qui est statique localement dans le fichier. Cette section est visible uniquement dans le fichier.
  • Pour rendre visible une partition de module ou de module importée, utilisez export import. Un exemple est illustré dans la section suivante.

Composer les partitions de module

Maintenant que nous avons les deux parties de l’API définie, nous allons les rassembler afin que les fichiers qui importent ce module puissent y accéder dans son ensemble.

Toutes les partitions de module doivent être exposées dans le cadre de la définition de module à laquelle elles appartiennent. Les partitions sont exposées dans l’interface du module principal. Ouvrez le BasicPlane.Figures.ixx fichier, qui définit l’interface du module principal. Remplacez son contenu par :

export module BasicPlane.Figures; // keywords export module marks this as a primary module interface unit

export import :Point; // bring in the Point partition, and export it to consumers of this module
export import :Rectangle; // bring in the Rectangle partition, and export it to consumers of this module

Les deux lignes qui commencent export import par sont nouvelles ici. Lorsqu’ils sont combinés comme ceci, ces deux mot clé indiquent au compilateur d’importer le module spécifié et de le rendre visible aux consommateurs de ce module. Dans ce cas, le signe deux-points (:) dans le nom du module indique que nous importons une partition de module.

Les noms importés n’incluent pas le nom complet du module. Par exemple, la :Point partition a été déclarée en tant que export module BasicPlane.Figures:Point. Pourtant, ici, nous importons :Point. Étant donné que nous sommes dans le fichier d’interface du module principal pour le module BasicPlane.Figures, le nom du module est implicite et seul le nom de la partition est spécifié.

Jusqu’à présent, nous avons défini l’interface du module principal, qui expose l’aire d’API que nous voulons rendre disponible. Mais nous n’avons déclaré, pas défini, area()height()ou width(). Nous allons le faire ensuite en créant un fichier d’implémentation de module.

Créer un fichier d’implémentation d’unité de module

Les fichiers d’implémentation d’unité de module ne se terminent pas par une .ixx extension. Ils sont des fichiers normaux .cpp . Ajoutez un fichier d’implémentation d’unité de module en créant un fichier source avec un clic droit dans le Explorateur de solutions dans les fichiers sources, sélectionnez Ajouter>un nouvel élément, puis sélectionnez Fichier C++ (.cpp). Donnez au nouveau fichier le nom BasicPlane.Figures-Rectangle.cpp, puis choisissez Ajouter.

La convention d’affectation de noms pour le fichier d’implémentation de la partition de module suit la convention d’affectation de noms d’une partition. Mais il a une .cpp extension, car il s’agit d’un fichier d’implémentation.

Remplacez le contenu du BasicPlane.Figures-Rectangle.cpp fichier par :

module;

// global module fragment area. Put #include directives here 

module BasicPlane.Figures:Rectangle;

int area(const Rectangle& r) { return width(r) * height(r); }
int height(const Rectangle& r) { return r.ul.y - r.lr.y; }
int width(const Rectangle& r) { return r.lr.x - r.ul.x; }

Ce fichier commence par module; l’introduction d’une zone spéciale du module appelée fragment de module global. Il précède le code du module nommé et est l’endroit où vous pouvez utiliser des directives de préprocesseur telles que #include. Le code dans le fragment de module global n’est pas détenu ou exporté par l’interface du module.

Lorsque vous incluez un fichier d’en-tête, vous ne souhaitez généralement pas qu’il soit traité comme une partie exportée du module. Vous incluez généralement le fichier d’en-tête comme détail d’implémentation qui ne doit pas faire partie de l’interface du module. Il peut y avoir des cas avancés où vous voulez le faire, mais en général vous ne le faites pas. Aucune métadonnées distincte (.ifc fichiers) n’est générée pour #include les directives dans le fragment de module global. Les fragments de module globaux offrent un bon emplacement pour inclure des fichiers d’en-tête tels que windows.h, ou sur Linux. unistd.h

Le fichier d’implémentation de module que nous créons n’inclut aucune bibliothèque, car il n’en a pas besoin dans le cadre de son implémentation. Mais si c’est le cas, c’est là que les #include directives iraient.

La ligne module BasicPlane.Figures:Rectangle; indique que ce fichier fait partie du module BasicPlane.Figuresnommé . Le compilateur place automatiquement les types et fonctions exposés par l’interface du module principal dans ce fichier. Une unité d’implémentation de module n’a pas le export mot clé avant la module mot clé dans sa déclaration de module.

Ensuite, voici la définition des fonctions area(), height()et width(). Ils ont été déclarés dans la Rectangle partition en BasicPlane.Figures-Rectangle.ixx. Étant donné que l’interface de module principale pour ce module a importé les Point partitions et Rectangle les partitions de module, ces types sont visibles ici dans le fichier d’implémentation de l’unité de module. Fonctionnalité intéressante des unités d’implémentation de module : le compilateur rend automatiquement tout ce qui se trouve dans l’interface principale du module correspondant visible par le fichier. Il n’est pas imports <module-name> nécessaire.

Tout ce que vous déclarez dans une unité d’implémentation n’est visible que par le module auquel il appartient.

Importation du module

Maintenant, nous allons utiliser le module que nous avons défini. Ouvrez le fichier ModulesTutorial.cpp . Il a été créé automatiquement dans le cadre du projet. Il contient actuellement la fonction main(). Remplacez son contenu par :

#include <iostream>

import BasicPlane.Figures;

int main()
{
    Rectangle r{ {1,8}, {11,3} };

    std::cout << "area: " << area(r) << '\n';
    std::cout << "width: " << width(r) << '\n';

    return 0;
}

L’instruction import BasicPlane.Figures; rend toutes les fonctions et types exportés du BasicPlane.Figures module visibles dans ce fichier. Il peut arriver avant ou après toute #include directive.

L’application utilise ensuite les types et fonctions du module pour générer la zone et la largeur du rectangle défini :

area: 50
width: 10

Anatomie d’un module

Examinons maintenant plus en détail les différents fichiers de module.

Interface du module principal

Un module se compose d’un ou plusieurs fichiers. L’une d’entre elles définit l’interface que les importateurs verront. Ce fichier contient l’interface du module principal. Il ne peut y avoir qu’une seule interface de module principale par module. Comme indiqué précédemment, l’unité d’interface de module exportée ne spécifie pas de partition de module.

Il a une .ixx extension par défaut. Toutefois, vous pouvez traiter un fichier source avec n’importe quelle extension en tant que fichier d’interface de module. Pour ce faire, définissez la propriété Compiler en tant que dans l’onglet Avancé de la page de propriétés du fichier source sur Compiler en tant que module (/interface) :

Screenshot of a hypothetical source file's Configuration properties under Configuration properties > C/C++ > Advanced > Compile As, with Compile as C++ Module Code (/interface) highlighted

Le plan de base d’un fichier de définition d’interface de module est :

module; // optional. Defines the beginning of the global module fragment

// #include directives go here but only apply to this file and
// aren't shared with other module implementation files.
// Macro definitions aren't visible outside this file, or to importers.
// import statements aren't allowed here. They go in the module preamble, below.

export module [module-name]; // Required. Marks the beginning of the module preamble

// import statements go here. They're available to all files that belong to the named module
// Put #includes in the global module fragment, above

// After any import statements, the module purview begins here
// Put exported functions, types, and templates here

module :private; // optional. The start of the private module partition.

// Everything after this point is visible only within this file, and isn't 
// visible to any of the other files that belong to the named module.

Ce fichier doit commencer par module; soit pour indiquer le début du fragment de module global, soit export module [module-name]; pour indiquer le début du purview du module.

Le module purview est l’endroit où les fonctions, les types, les modèles, et ainsi de suite, vous souhaitez exposer à partir du module.

C’est également là que vous pouvez exposer d’autres modules ou partitions de module via les export import mot clé, comme indiqué dans le BasicPlane.Figures.ixx fichier.

Le fichier d’interface primaire doit exporter toutes les partitions d’interface définies pour le module directement ou indirectement, ou le programme n’est pas formé.

La partition de module privé est l’endroit où vous pouvez placer des éléments que vous souhaitez uniquement afficher dans ce fichier.

Les unités d’interface de module précédent la mot clé module avec le mot clé export.

Pour plus d’informations sur la syntaxe des modules, consultez Modules.

Unités d’implémentation de module

Les unités d’implémentation de module appartiennent à un module nommé. Le module nommé auquel ils appartiennent est indiqué par l’instruction module [module-name] dans le fichier. Les unités d’implémentation de module fournissent des détails d’implémentation qui, pour des raisons d’hygiène du code ou d’autres raisons, vous ne souhaitez pas placer dans l’interface du module principal ou dans un fichier de partition de module.

Les unités d’implémentation de module sont utiles pour diviser un module volumineux en petits éléments, ce qui peut entraîner des temps de génération plus rapides. Cette technique est abordée brièvement dans la section Meilleures pratiques .

Les fichiers d’unité d’implémentation de module ont une .cpp extension. La structure de base d’un fichier d’unité d’implémentation de module est la suivante :

// optional #include or import statements. These only apply to this file
// imports in the associated module's interface are automatically available to this file

module [module-name]; // required. Identifies which named module this implementation unit belongs to

// implementation

Fichiers de partition de module

Les partitions de module permettent de composanter un module en différents éléments ou partitions. Les partitions de module sont destinées à être importées uniquement dans les fichiers qui font partie du module nommé. Ils ne peuvent pas être importés en dehors du module nommé.

Une partition a un fichier d’interface et zéro ou plusieurs fichiers d’implémentation. Une partition de module partage la propriété de toutes les déclarations dans l’ensemble du module.

Tous les noms exportés par les fichiers d’interface de partition doivent être importés et re-exportés (export import) par le fichier d’interface primaire. Le nom d’une partition doit commencer par le nom du module, suivi d’un signe deux-points, puis le nom de la partition.

Le plan de base d’un fichier d’interface de partition ressemble à ceci :

module; // optional. Defines the beginning of the global module fragment

// This is where #include directives go. They only apply to this file and aren't shared
// with other module implementation files.
// Macro definitions aren't visible outside of this file or to importers
// import statements aren't allowed here. They go in the module preamble, below

export module [Module-name]:[Partition name]; // Required. Marks the beginning of the module preamble

// import statements go here. 
// To access declarations in another partition, import the partition. Only use the partition name, not the module name.
// For example, import :Point;
// #include directives don't go here. The recommended place is in the global module fragment, above

// export imports statements go here

// after import, export import statements, the module purview begins
// put exported functions, types, and templates for the partition here

module :private; // optional. Everything after this point is visible only within this file, and isn't 
                         // visible to any of the other files that belong to the named module.
...

Bonnes pratiques relatives aux modules

Un module et le code qui l’importe doivent être compilés avec les mêmes options de compilateur.

Nommage du module

  • Vous pouvez utiliser des points ('.') dans vos noms de module, mais ils n’ont aucune signification particulière pour le compilateur. Utilisez-les pour transmettre la signification aux utilisateurs de votre module. Par exemple, commencez par l’espace de noms supérieur de la bibliothèque ou du projet. Terminez par un nom qui décrit la fonctionnalité du module. BasicPlane.Figures est destiné à transmettre une API pour les plans géométriques, et spécifiquement des figures qui peuvent être représentées sur un plan.
  • Le nom du fichier qui contient l’interface principale du module est généralement le nom du module. Par exemple, étant donné le nom BasicPlane.Figuresdu module, le nom du fichier contenant l’interface primaire serait nommé BasicPlane.Figures.ixx.
  • Le nom d’un fichier de partition de module est généralement <primary-module-name>-<module-partition-name> l’endroit où le nom du module est suivi d’un trait d’union ('-'), puis du nom de la partition. Par exemple, BasicPlane.Figures-Rectangle.ixx

Si vous générez à partir de la ligne de commande et que vous utilisez cette convention d’affectation de noms pour les partitions de module, vous n’avez pas à ajouter /reference explicitement pour chaque fichier de partition de module. Le compilateur les recherche automatiquement en fonction du nom du module. Le nom du fichier de partition compilé (se terminant par une .ifc extension) est généré à partir du nom du module. Considérez le nom BasicPlane.Figures:Rectangledu module : le compilateur prévoit que le fichier de partition compilé correspondant est Rectangle nommé BasicPlane.Figures-Rectangle.ifc. Le compilateur utilise ce schéma d’affectation de noms pour faciliter l’utilisation des partitions de module en recherchant automatiquement les fichiers d’unité d’interface pour les partitions.

Vous pouvez les nommer à l’aide de votre propre convention. Toutefois, vous devez spécifier les arguments correspondants /reference au compilateur de ligne de commande.

Modules factor

Utilisez des fichiers et des partitions d’implémentation de module pour prendre en compte votre module pour faciliter la maintenance du code et accélérer potentiellement les temps de compilation.

Par exemple, le déplacement de l’implémentation d’un module hors du fichier de définition de l’interface de module et dans un fichier d’implémentation de module signifie que les modifications apportées à l’implémentation n’entraînent pas nécessairement la recompilation de chaque fichier qui importe le module (sauf si vous avez inline des implémentations).

Les partitions de module facilitent la prise en compte logique d’un module volumineux. Ils peuvent être utilisés pour améliorer le temps de compilation afin que les modifications apportées à une partie de l’implémentation n’entraînent pas la recompilation de tous les fichiers du module.

Résumé

Dans ce tutoriel, vous avez été introduit dans les concepts de base des modules C++20. Vous avez créé une interface de module primaire, défini une partition de module et créé un fichier d’implémentation de module.

Voir aussi

Vue d’ensemble des modules en C++
module, importmot clé export s
Visite guidée des modules C++ dans Visual Studio
Modules C++20 pratiques et avenir des outils autour des modules C++
Déplacement d’un projet vers des modules nommés C++
Procédure pas à pas : Générer et importer des unités d’en-tête dans Microsoft Visual C++