Compartir a través de


Tutorial de módulos con nombre (C++)

Este tutorial trata sobre la creación de módulos de C++20. Los módulos reemplazan los archivos de encabezado. Obtendrá información sobre cómo los módulos son una mejora respecto a los archivos de encabezado.

En este tutorial, aprenderá a:

  • Creación e importación de un módulo
  • Creación de una unidad de interfaz principal del módulo
  • Creación de un archivo de partición de módulo
  • Creación de un archivo de implementación de unidad de módulo

Requisitos previos

Este tutorial requiere Visual Studio 2022 17.1.0 o posterior.

Es posible que obtenga errores de IntelliSense mientras trabaja en el ejemplo de código de este tutorial. El trabajo en el motor de IntelliSense se está actualizando con el compilador. Los errores de IntelliSense se pueden omitir y no impedirán que se compile el ejemplo de código. Para realizar un seguimiento del progreso en el trabajo de IntelliSense, consulte este problema.

Qué son los módulos de C++

Los archivos de encabezado indican cómo se comparten las declaraciones y las definiciones entre archivos de código fuente en C++. Los archivos de encabezado son frágiles y difíciles de componer. Pueden compilarse de forma diferente en función del orden en que los incluya o en las macros que son o no están definidas. Pueden ralentizar el tiempo de compilación porque se vuelven a procesar para cada archivo de código fuente que los incluya.

C++20 presenta un enfoque moderno para la creación de componentes a partir de programas de C++: los módulos.

Al igual que los archivos de encabezado, los módulos permiten compartir declaraciones y definiciones entre archivos de código fuente. Pero a diferencia de los archivos de encabezado, los módulos no filtran definiciones de macros ni detalles privados de la implementación.

Los módulos son más fáciles de componer porque su semántica no cambia debido a definiciones de macros o a lo que se ha importado, el orden de las importaciones, etc. También facilitan el control de lo que es visible para los consumidores.

Los módulos proporcionan garantías de seguridad adicionales que los archivos de encabezado no proporcionan. El compilador y el enlazador funcionan juntos para evitar posibles problemas de colisión de nombres y proporcionar garantías más sólidas de una regla de definición (ODR).

Un modelo de propiedad segura evita conflictos entre nombres en el momento del enlazado porque el enlazador adjunta los nombres exportados al módulo que los exporta. Este modelo permite que el compilador de Microsoft Visual C++ evite un comportamiento indefinido causado por el enlazado de distintos módulos que notifican nombres similares en el mismo programa. Para obtener más información, consulte Propiedad segura.

Un módulo se compone de uno o varios archivos de código fuente compilados en un archivo binario. El archivo binario describe todos los tipos, funciones y plantillas exportados del módulo. Cuando un archivo de código fuente importa un módulo, el compilador lee en el archivo binario que tiene el contenido del módulo. Leer el archivo binario es mucho más rápido que procesar un archivo de encabezado. Además, el compilador reutiliza el archivo binario cada vez que se importa el módulo, lo que ahorra aún más tiempo. Dado que un módulo se compila una vez en lugar de cada vez que se importa, se puede reducir el tiempo de compilación, a veces drásticamente.

Lo más importante es que los módulos no tengan los problemas de fragilidad que hacen los archivos de encabezado. La importación de un módulo no cambia la semántica del módulo ni la semántica de ningún otro módulo importado. Las macros, las directivas de preprocesador y los nombres no exportados declarados en un módulo no son visibles para el archivo de código fuente que lo importa. Puede importar módulos en cualquier orden y no cambiará el significado de los módulos.

Los módulos se pueden usar en paralelo con los archivos de encabezado. Esta característica es cómoda si va a migrar una base de código para usar módulos porque puede hacerlo en fases.

En algunos casos, un archivo de encabezado se puede importar como una unidad de encabezado en lugar de como un archivo #include. Las unidades de encabezado son la alternativa recomendada a los archivos de encabezado precompilado (PCH). Son más fáciles de configurar y de usar que los archivos PCH compartidos, pero proporcionan ventajas de rendimiento similares. Para obtener más información, consulte Tutorial: Compilación e importación de unidades de encabezado en Microsoft Visual C++.

El código puede consumir módulos en el mismo proyecto, o cualquier proyecto al que se hace referencia, mediante referencias de proyecto a proyecto a proyectos estáticos.

Creación del proyecto

A medida que compilamos un proyecto sencillo, veremos varios aspectos de los módulos. El proyecto implementará una API mediante un módulo en lugar de un archivo de encabezado.

En Visual Studio 2022 o posterior, elija Crear un nuevo proyecto y, después, el tipo de proyecto Aplicación de consola (para C++). Si este tipo de proyecto no está disponible, es posible que no haya seleccionado la carga de trabajo Desarrollo de escritorio con C++ al instalar Visual Studio. Puede usar el Instalador de Visual Studio para agregar la carga de trabajo de C++.

Asigne al nuevo proyecto el nombre ModulesTutorial y cree el proyecto.

Dado que los módulos son una característica de C++20, use la opción del compilador o /std:c++latest ./std:c++20 En el Explorador de soluciones, haga clic con el botón derecho en el nombre ModulesTutorialdel proyecto y elija Propiedades. En el cuadro de diálogo Páginas de propiedades del proyecto, cambie Configuración a Todas las configuraciones y Plataforma a Todas las plataformas. Seleccione Propiedades de configuración>General en el panel de vista de árbol de la izquierda. Seleccione la propiedad Estándar del lenguaje C++. Use la lista desplegable para cambiar el valor de la propiedad a Estándar ISO C++20 (/std:c++20). Seleccione Aceptar para aceptar el cambio.

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

Creación de la unidad de interfaz principal del módulo

Un módulo consta de uno o varios archivos. Uno de estos archivos debe ser lo que se conoce como unidad de interfaz principal del módulo. Define lo que exporta el módulo; es decir, lo que verán los importadores del módulo. Solo puede haber una unidad de interfaz de módulo principal por módulo.

Para agregar una unidad de interfaz de módulo principal, en Explorador de soluciones, haga clic con el botón derecho en Archivos de origen y seleccione Agregar>módulo.

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

En el cuadro de diálogo Agregar nuevo elemento que aparece, asigne al nuevo módulo el nombre BasicPlane.Figures.ixx y elija Agregar.

El contenido predeterminado del archivo de módulo creado tiene dos líneas:

export module BasicPlane;

export void MyFunc();

Las export module palabras clave de la primera línea declaran que este archivo es una unidad de interfaz de módulo. Hay un punto sutil aquí: para cada módulo con nombre, debe haber exactamente una unidad de interfaz de módulo sin ninguna partición de módulo especificada. Esa unidad del módulo se llama unidad de interfaz principal del módulo.

La unidad de interfaz principal del módulo es donde se declaran las funciones, los tipos, las plantillas, otros módulos y las particiones de módulo que se van a exponer cuando los archivos de código fuente importan el módulo. Un módulo puede constar de varios archivos, pero solo el archivo de interfaz principal del módulo identifica lo que se va a exponer.

Reemplace el contenido del BasicPlane.Figures.ixx archivo por:

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

Esta línea identifica este archivo como la interfaz principal del módulo y proporciona al módulo un nombre: BasicPlane.Figures. El punto del nombre del módulo no tiene ningún significado especial para el compilador. Se puede usar un punto para transmitir cómo está organizado el módulo. Si tiene varios archivos de módulo que funcionan juntos, puede usar puntos para indicar una separación de intereses. En este tutorial, usaremos puntos para indicar las diferentes áreas funcionales de la API.

Este nombre también es donde procede el "con nombre" de "módulo con nombre". Los archivos que forman parte de este módulo usan este nombre para identificarse como parte del módulo con nombre. Un módulo con nombre es la colección de unidades de módulo con el mismo nombre de módulo.

Deberíamos hablar un momento sobre la API que vamos a implementar antes de continuar. Afecta a las decisiones que hacemos a continuación. La API representa diferentes formas. Solo vamos a proporcionar un par de formas en este ejemplo: Point y Rectangle. Point está pensado para usarse como parte de formas más complejas, como Rectangle.

Para ilustrar algunas características de los módulos, vamos a factorizar esta API en partes. Una parte será la Point API. La otra parte será la relativa a Rectangle. Imagine que esta API va a crecer para convertirse en algo más complejo. La división es útil para separar problemas o facilitar el mantenimiento del código.

Hasta ahora, hemos creado la interfaz principal del módulo que expondrá esta API. Ahora, vamos a crear la API Point. Queremos que forme parte de este módulo. Por motivos de organización lógica y posible eficacia de la compilación, queremos que esta parte de la API sea fácil de entender por sí misma. Para ello, crearemos un archivo de partición de módulo.

Un archivo de partición de módulo es una parte, o un componente, de un módulo. Lo que hace que sea único es que se puede tratar como una parte individual del módulo, pero solo dentro del módulo. Las particiones de módulo no se pueden consumir fuera de un módulo. Las particiones de módulo son útiles para dividir la implementación del módulo en partes manejables.

Al importar una partición en el módulo principal, todas sus declaraciones son visibles para el módulo principal, independientemente de si se exportan. Las particiones se pueden importar en cualquier interfaz de partición, interfaz principal del módulo o unidad de módulo que pertenezca al módulo con nombre.

Creación de un archivo de partición de módulo

Partición de módulo Point

Para crear un archivo de partición de módulo, en el Explorador de soluciones haga clic con el botón derecho en Archivos de origen y, a continuación, seleccione Agregar>módulo. Asigne el nombre al archivo BasicPlane.Figures-Point.ixx y seleccione Agregar.

Dado que es un archivo de partición de módulo, hemos agregado un guión y el nombre de la partición al nombre del módulo. Esta convención ayuda al compilador en el caso de la línea de comandos porque el compilador usa reglas de búsqueda de nombres basadas en el nombre del módulo para buscar el archivo compilado .ifc para la partición. De este modo, no es necesario proporcionar argumentos de la línea de comandos de tipo /reference explícitos para buscar las particiones que pertenecen al módulo. También resulta útil organizar los archivos que pertenecen a un módulo por su nombre, ya que puede ver fácilmente qué archivos pertenecen a los módulos.

Reemplace el contenido de BasicPlane.Figures-Point.ixx por:

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

export struct Point
{
    int x, y;
};

El archivo empieza con export module. Estas palabras clave también son cómo comienza la interfaz del módulo principal. Lo que hace que este archivo sea diferente es el signo de dos puntos (:) que sigue al nombre del módulo, seguido del nombre de la partición. Esta convención de nomenclatura identifica el archivo como una partición de módulo. Dado que define la interfaz del módulo para una partición, no se considera la interfaz principal del módulo.

El nombre BasicPlane.Figures:Point identifica esta partición como parte del módulo BasicPlane.Figures. (Recuerde que el punto del nombre no tiene ningún significado especial para el compilador). Los dos puntos indican que este archivo contiene una partición de módulo denominada Point que pertenece al módulo BasicPlane.Figures. Podemos importar esta partición en otros archivos que formen parte de este módulo con nombre.

En este archivo, la palabra clave export hace que struct Point sea visible para los consumidores.

Partición de módulo Rectangle

La siguiente partición que definiremos es Rectangle. Cree otro archivo de módulo con los mismos pasos que antes: en Explorador de soluciones, haga clic con el botón derecho en Archivos de origen y seleccione Agregar>módulo. Asigne el nombre BasicPlane.Figures-Rectangle.ixx al archivo y seleccione Agregar.

Reemplace el contenido de BasicPlane.Figures-Rectangle.ixx por:

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);

El archivo comienza con export module BasicPlane.Figures:Rectangle; el que declara una partición de módulo que forma parte del módulo BasicPlane.Figures. El elemento :Rectangle agregado al nombre del módulo lo define como una partición del módulo BasicPlane.Figures. Se puede importar individualmente en cualquiera de los archivos de módulo que forman parte de este módulo con nombre.

A continuación, import :Point; muestra cómo importar una partición de módulo. La instrucción import hace que todos los tipos, funciones y plantillas exportados de la partición de módulo sean visibles para el módulo. No es necesario especificar el nombre del módulo. El compilador sabe que este archivo pertenece al módulo BasicPlane.Figures debido al elemento export module BasicPlane.Figures:Rectangle; de la parte superior del archivo.

A continuación, el código exporta la definición de struct Rectangle y las declaraciones de algunas funciones que devuelven varias propiedades del rectángulo. La palabra clave export indica si se debe hacer visible aquello a lo que precede para los consumidores del módulo. Se usa para hacer que las funciones area, height y width sean visibles fuera del módulo.

Todas las definiciones y declaraciones de una partición de módulo son visibles para la unidad de módulo de importación, tanto si tienen la export palabra clave como si no. La palabra clave export rige si la definición, la declaración o la definición de tipo estarán visibles fuera del módulo al exportar la partición en la interfaz principal del módulo.

Los nombres se hacen visibles para los consumidores de un módulo de varias maneras:

  • Coloque la palabra clave export delante de cada tipo, función, etc., que quiera exportar.
  • Si coloca export delante de un espacio de nombres, por ejemplo export namespace N { ... }, se exporta todo lo definido dentro de las llaves. Sin embargo, si en otra parte del módulo define namespace N { struct S {...};}, struct S no está disponible para los consumidores del módulo. No está disponible porque esa declaración de espacio de nombres no está precedida por export, aunque haya otro espacio de nombres con el mismo nombre.
  • Si no se debe exportar un tipo, una función, etc., omita la palabra clave export. Será visible para otros archivos que forman parte del módulo, pero no para los importadores del módulo.
  • Use module :private; para marcar el principio de la partición privada de módulo. La partición privada de módulo es una sección del módulo donde las declaraciones solo son visibles para ese archivo. No son visibles para los archivos que importan este módulo ni para otros archivos que forman parte de este módulo. Piense en ella como una sección que sea estática local para el archivo. Esta sección solo está visible en el archivo.
  • Para que un módulo o partición de módulo importado sea visible, use export import. En la sección siguiente, se muestra un ejemplo.

Redacción de las particiones de módulo

Ahora que tenemos las dos partes de la API definidas, vamos a reunirlas para que los archivos que importen este módulo puedan acceder a ellas en su conjunto.

Todas las particiones de módulo se deben exponer como parte de la definición del módulo al que pertenecen. Las particiones se exponen en la interfaz principal del módulo. Abra el archivo BasicPlane.Figures.ixx, que define la interfaz principal del módulo. Reemplace su contenido por lo siguiente:

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

Las dos líneas que comienzan por export import son nuevas aquí. Cuando se combina de esta manera, estas dos palabras clave indican al compilador que importe el módulo especificado y que sea visible para los consumidores de este módulo. En este caso, los dos puntos (:) del nombre del módulo indican que estamos importando una partición de módulo.

Los nombres importados no incluyen el nombre completo del módulo. Por ejemplo, la partición :Point se declaró como export module BasicPlane.Figures:Point. Sin embargo, aquí vamos a importar :Point. Dado que estamos en el archivo de interfaz del módulo principal para el módulo , el nombre del módulo BasicPlane.Figuresestá implícito y solo se especifica el nombre de partición.

Hasta ahora, hemos definido la interfaz principal del módulo, que expone la superficie de API que queremos que esté disponible. Pero solo hemos declarado, no definido, area(), height() y width(). Lo haremos a continuación mediante la creación de un archivo de implementación de módulo.

Creación de un archivo de implementación de unidad de módulo

Los archivos de implementación de unidad de módulo no terminan con una .ixx extensión, ya que son archivos normales .cpp . Para agregar un archivo de implementación de unidad de módulo, cree un archivo de código fuente mediante un clic con el botón derecho en el Explorador de soluciones en Archivos de código fuente, seleccione Agregar>Nuevo elemento y, a continuación, seleccione Archivo de C++ (.cpp). Asigne al nuevo archivo el nombre BasicPlane.Figures-Rectangle.cpp y elija Agregar.

La convención de nomenclatura del archivo de implementación de la partición del módulo sigue la convención de nomenclatura de una partición. Pero tiene la extensión .cpp porque es un archivo de implementación.

Reemplace el contenido del archivo BasicPlane.Figures-Rectangle.cpp por lo siguiente:

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; }

Este archivo comienza con module; el que se presenta un área especial del módulo denominado fragmento de módulo global. Precede al código del módulo con nombre y es donde puede usar directivas de preprocesador como #include. El código del fragmento de módulo global no es propiedad de la interfaz del módulo ni la exporta.

Al incluir un archivo de encabezado, normalmente no querrá que se trate como una parte exportada del módulo. Normalmente, el archivo de encabezado se incluye como un detalle de implementación que no debe formar parte de la interfaz del módulo. Puede haber casos avanzados en los que quiera hacerlo, pero generalmente no será así. No se generan metadatos independientes (archivos .ifc) para las directivas #include del fragmento global del módulo. Los fragmentos globales del módulo proporcionan un buen lugar para incluir archivos de encabezado como windows.h o, en Linux, unistd.h.

El archivo de implementación del módulo que estamos creando no incluye ninguna biblioteca porque no las necesita como parte de su implementación. Pero si las necesita, esta área es donde irían las directivas #include.

La línea module BasicPlane.Figures:Rectangle; indica que este archivo forma parte del módulo BasicPlane.Figurescon nombre . El compilador trae automáticamente a este archivo las funciones y los tipos expuestos por la interfaz principal del módulo. Una unidad de implementación de módulo no tiene la export palabra clave antes de la module palabra clave en su declaración de módulo.

A continuación, está la definición de las funciones area(), height() y width(). Se declararon en la partición Rectangle en BasicPlane.Figures-Rectangle.ixx. Dado que la interfaz principal de este módulo importó las particiones de módulo Point y Rectangle, esos tipos están visibles aquí en el archivo de implementación de unidad de módulo. Una característica interesante de las unidades de implementación de módulo: el compilador hace que todo lo que hay en la interfaz principal del módulo correspondiente sea visible para el archivo. No es necesario utilizar imports <module-name>.

Todo lo que declare dentro de una unidad de implementación solo es visible para el módulo al que pertenece.

Importación del módulo

Ahora, vamos a usar el módulo que hemos definido. Abra el archivo ModulesTutorial.cpp . Se creó automáticamente como parte del proyecto. Actualmente, contiene la función main(). Reemplace su contenido por lo siguiente:

#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;
}

La instrucción import BasicPlane.Figures; hace que todas las funciones y tipos exportados del BasicPlane.Figures módulo estén visibles para este archivo. Puede venir antes o después de cualquier #include directiva.

A continuación, la aplicación usa los tipos y funciones del módulo para generar como salida el área y el ancho del rectángulo definido:

area: 50
width: 10

Anatomía de un módulo

Ahora, veamos con más detalle los distintos archivos de módulo.

Interfaz principal del módulo

Un módulo consta de uno o varios archivos. Uno de ellos define la interfaz que verán los importadores. Este archivo contiene la interfaz principal del módulo. Solo puede haber una interfaz principal del módulo por cada módulo. Como se indicó anteriormente, la unidad de interfaz del módulo exportado no especifica una partición de módulo.

Tiene la extensión .ixx de manera predeterminada. Sin embargo, puede tratar un archivo de código fuente con cualquier extensión como un archivo de interfaz de módulo. Para ello, establezca la propiedad Compilar como en la pestaña Opciones avanzadas de la página de propiedades del archivo de origen en Compile As 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

El esquema básico de un archivo de definición de interfaz de módulo es el siguiente:

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.

Este archivo debe comenzar con module; para indicar el principio del fragmento global del módulo o con export module [module-name]; para indicar el inicio del ámbito del módulo.

El módulo purview es donde las funciones, los tipos, las plantillas, etc., van a exponer desde el módulo.

También es donde puede exponer otros módulos o particiones de módulo mediante las palabras clave export import, como se muestra en el archivo BasicPlane.Figures.ixx.

El archivo de interfaz principal debe exportar todas las particiones de interfaz definidas para el módulo directamente o indirectamente, o bien el programa tiene un formato incorrecto.

La partición privada del módulo es donde puede colocar los elementos que desea que solo sean visibles en este archivo.

Las unidades de interfaz de módulo usan como prefijo de la palabra clave module la palabra clave export.

Para obtener una visión más detallada de la sintaxis del módulo, consulte Introducción a los módulos en C++.

Unidades de implementación de módulo

Las unidades de implementación de módulo pertenecen a un módulo con nombre. El módulo con nombre al que pertenecen se indica mediante la module [module-name] instrucción del archivo . Las unidades de implementación de módulo proporcionan detalles de implementación que, por motivos de higiene del código u otros motivos, no desea colocar en la interfaz principal del módulo ni en un archivo de partición de módulo.

Las unidades de implementación de módulos son útiles para dividir un módulo grande en partes más pequeñas, lo que puede dar lugar a tiempos de compilación más rápidos. Esta técnica se trata brevemente en la sección Procedimientos recomendados para módulos.

Los archivos de unidad de implementación de módulo tienen la extensión .cpp. El esquema básico de un archivo de unidad de implementación de módulo es el siguiente:

// 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

Archivos de partición de módulo

Las particiones de módulo proporcionan una manera de componentes de un módulo en diferentes partes o particiones. Las particiones de módulo están pensadas para su importación solo en archivos que forman parte del módulo con nombre. No se pueden importar fuera del módulo con nombre.

Una partición tiene un archivo de interfaz y cero o más archivos de implementación. Una partición de módulo comparte la propiedad de todas las declaraciones del módulo completo.

Todos los nombres exportados por los archivos de interfaz de partición son importados y reexportados (export import) por el archivo de la interfaz principal. El nombre de una partición debe comenzar con el nombre del módulo, seguido de dos puntos y, a continuación, el nombre de la partición.

El esquema básico de un archivo de interfaz de partición tiene este aspecto:

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.
...

Procedimientos recomendados del módulo

Un módulo y el código que lo importa se deben compilar con las mismas opciones del compilador.

Nomenclatura de los módulos

  • Puede usar puntos (".") en los nombres de módulo, pero no tienen ningún significado especial para el compilador. Úselos para transmitir el significado a los usuarios del módulo. Por ejemplo, comience con el espacio de nombres de nivel superior del proyecto o la biblioteca. Finalice con un nombre que describa la funcionalidad del módulo. BasicPlane.Figures está pensado para transmitir una API para planos geométricos, y específicamente figuras que se pueden representar en un plano.
  • El nombre del archivo que contiene la interfaz principal del módulo suele ser el nombre del módulo. Por ejemplo, dado el nombre de módulo BasicPlane.Figures, el nombre del archivo que contiene la interfaz principal se llamaría BasicPlane.Figures.ixx.
  • El nombre de un archivo de partición de módulo suele ser <primary-module-name>-<module-partition-name>, donde el nombre del módulo va seguido de un guión ("-") y, a continuación, el nombre de la partición. Por ejemplo: BasicPlane.Figures-Rectangle.ixx

Si va a compilar desde la línea de comandos y usa esta convención de nomenclatura para las particiones de módulo, no tendrá que agregar explícitamente /reference para cada archivo de partición de módulo. El compilador los buscará automáticamente en función del nombre del módulo. El nombre del archivo de partición compilado (que termina con una .ifc extensión) se genera a partir del nombre del módulo. Considere el nombre BasicPlane.Figures:Rectangledel módulo : el compilador prevé que el archivo de partición compilado correspondiente para Rectangle se denomina BasicPlane.Figures-Rectangle.ifc. El compilador usa este esquema de nomenclatura para facilitar el uso de particiones de módulo mediante la búsqueda automática de los archivos de unidad de interfaz para las particiones.

Puede asignarles un nombre mediante su propia convención. Sin embargo, deberá especificar los argumentos /reference correspondientes al compilador de la línea de comandos.

Módulos de factor

Use archivos de implementación de módulo y particiones para factorizar el módulo para facilitar el mantenimiento del código y tener tiempos de compilación potencialmente más rápidos.

Por ejemplo, mover la implementación de un módulo fuera del archivo de definición de interfaz de módulo y a un archivo de implementación de módulo significa que los cambios en la implementación no harán necesariamente que todos los archivos que importan el módulo se vuelvan a compilar (a menos que tenga implementaciones inline).

Las particiones de módulo facilitan la factorización lógica de un módulo grande. Se pueden usar para mejorar el tiempo de compilación para que los cambios realizados en una parte de la implementación no provoquen que se vuelvan a compilar todos los archivos del módulo.

Resumen

En este tutorial, se han presentado los conceptos básicos de los módulos de C++20. Ha creado una interfaz principal del módulo, ha definido una partición de módulo y ha creado un archivo de implementación de módulo.

Consulte también

Información general de los módulos en C++
Palabras clave module, import y export
Un recorrido por los módulos de C++ en Visual Studio
Módulos de C++20 en la práctica y el futuro de las herramientas en torno a los módulos de C++
Traslado de un proyecto a módulos con nombre de C++
Tutorial: Compilación e importación de unidades de encabezado en Microsoft Visual C++