Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Obtenga información sobre cómo importar la biblioteca estándar de C++ mediante módulos de biblioteca de C++. Esto da como resultado una compilación más rápida y es más sólida que usar archivos de encabezado o unidades de encabezado o encabezados precompilados (PCH).
En este tutorial, obtenga información sobre:
- Cómo importar la biblioteca estándar como módulo desde la línea de comandos.
- Ventajas de rendimiento y facilidad de uso de los módulos.
- Los dos módulos de biblioteca estándar,
stdystd.compat, y la diferencia entre ellos.
Prerrequisitos
Este tutorial requiere Visual Studio 2022 17.5 o posterior.
Introducción a los módulos de biblioteca estándar
La semántica de los archivos header puede cambiar según las definiciones de macros, el orden en el que se incluyan y ralentizan la compilación. Los módulos resuelven estos problemas.
Ahora es posible importar la biblioteca estándar como módulo en lugar de como un conjunto de archivos de encabezado. Esto es mucho más rápido y sólido que incluir archivos de encabezado, unidades de encabezado o encabezados precompilados (PCH).
La biblioteca estándar de C++23 presenta dos módulos con nombre: std y std.compat:
-
stdexporta las declaraciones y los nombres definidos en elstdnamespace de la biblioteca estándar de C++, comostd::vector. También exporta el contenido de los encabezados de contenedor de C, como<cstdio>y<cstdlib>, que proporcionan funciones comostd::printf(). Las funciones de C definidas en el espacio de nombres global, como::printf(), no se exportan. Esto mejora la situación en la que incluir un encabezado contenedor de C como<cstdio>también incluye archivos de encabezado de C comostdio.h, que aportan las versiones del espacio de nombres global de C. Esto no es un problema si importastd. -
std.compatexporta todo lo que hay enstdy agrega los espacios de nombres globales del entorno de ejecución de C, como::printf,::fopen,::size_t,::strlenetc. El módulostd.compatfacilita el trabajo con bases de código que hacen referencia a muchas funciones y tipos del tiempo de ejecución de C en el espacio de nombres global.
El compilador importa toda la biblioteca estándar cuando se usa import std; o import std.compat; y lo hace más rápido que incorporar un único archivo de encabezado. Es más rápido incorporar toda la biblioteca estándar con import std; (o import std.compat) que a #include <vector>, por ejemplo.
Dado que los módulos con nombre no exponen macros, las macros como assert, errnooffsetof, , va_argy otras no están disponibles al importar std o std.compat. Consulte Consideraciones sobre módulos con nombre de biblioteca estándar para obtener soluciones alternativas.
Acerca de los módulos de C++
Los archivos de encabezado son cómo se han compartido declaraciones y definiciones entre archivos de origen en C++. Antes de los módulos de biblioteca estándar, incluiría la parte de la biblioteca estándar que necesitaba con una directiva como #include <vector>. Los archivos de encabezado son frágiles y difíciles de componer porque su semántica puede cambiar en función del orden en que los incluya o si se definen determinadas macros. También ralentizan la compilación porque cada archivo fuente los procesa de nuevo.
C++20 presenta una alternativa moderna denominada módulos. En C++23, pudimos poner en mayúscula la compatibilidad con módulos para introducir módulos con nombre para representar la biblioteca estándar.
Al igual que los archivos de encabezado, los módulos permiten compartir declaraciones y definiciones entre archivos de origen. Pero a diferencia de los archivos de encabezado, los módulos no son frágiles y son más fáciles de componer porque su semántica no cambia debido a definiciones de macros o al orden en el que se importan. El compilador puede procesar módulos mucho más rápido de lo que puede procesar #include archivos y también usa menos memoria en tiempo de compilación. Los módulos con nombre no exponen definiciones de macro ni detalles de implementación privada.
En este artículo se muestra la nueva y mejor manera de consumir la biblioteca estándar. Para obtener más información sobre las formas alternativas de consumir la biblioteca estándar, vea Comparar unidades de encabezado, módulos y encabezados precompilados.
Importación de la biblioteca estándar con std
En los ejemplos siguientes se muestra cómo consumir la biblioteca estándar como módulo mediante el compilador de línea de comandos. Para obtener información sobre cómo hacerlo en el IDE de Visual Studio, consulte Compilación de módulos de biblioteca estándar iso C++23.
La instrucción import std; o import std.compat; importa la biblioteca estándar en la aplicación. Pero en primer lugar, debe compilar la biblioteca estándar denominada modules en formato binario. Los pasos siguientes muestran cómo hacerlo.
Ejemplo: Cómo construir e importar std
Abra un símbolo del sistema de herramientas nativas x86 para VS: en el menú Inicio de Windows, escriba x86 nativo y el símbolo del sistema debería aparecer en la lista de aplicaciones. Asegúrese de que la solicitud es para Visual Studio 2022, versión 17.5 o posterior. Si utiliza la versión incorrecta del aviso, obtendrá errores. Los ejemplos usados en este tutorial son para el shell de CMD.
Cree un directorio, como
%USERPROFILE%\source\repos\STLModules, y consítelo como el directorio actual. Si elige un directorio al que no tiene acceso de escritura, obtendrá errores durante la compilación.Compile el
stdmódulo con nombre con el siguiente comando:cl /std:c++latest /EHsc /nologo /W4 /c "%VCToolsInstallDir%\modules\std.ixx"Si recibe errores, asegúrese de que está usando la versión adecuada del símbolo del sistema.
Compile el módulo
stddenominado utilizando la misma configuración del compilador que pretende usar con el código que importa el módulo compilado. Si tiene una solución de varios proyectos, puede compilar la biblioteca estándar denominada módulo una vez y, a continuación, hacer referencia a ella desde todos los proyectos mediante la/referenceopción del compilador.Con el comando anterior del compilador, el compilador genera dos archivos:
-
std.ifces la representación binaria compilada de la interfaz del módulo con nombre que el compilador consulta para procesar laimport std;instrucción . Se trata de un artefacto solo durante el tiempo de compilación. No se envía con tu aplicación. -
std.objcontiene la implementación del módulo especificado. Agreguestd.obja la línea de comandos al compilar la aplicación de ejemplo para vincular estáticamente la funcionalidad que usa desde la biblioteca estándar a la aplicación.
Los modificadores de la línea de comandos clave de este ejemplo son:
Conmutador Meaning /std:c++latestUse la versión más reciente del estándar y la biblioteca del lenguaje C++. Aunque la compatibilidad con módulos está disponible en /std:c++20, necesita la biblioteca estándar más reciente para obtener compatibilidad con la biblioteca estándar denominada módulos./EHscUse el control de excepciones de C++, excepto para las funciones marcadas como extern "C"./W4Por lo general, se recomienda usar /W4, especialmente para los nuevos proyectos, ya que permite todas las advertencias de nivel 1, nivel 2, nivel 3 y la mayoría de los niveles 4 (informativos), lo que puede ayudarle a detectar posibles problemas al principio. Básicamente proporciona advertencias similares a las que ofrece un análisis lint, que pueden ayudar a asegurar que haya la menor cantidad posible de defectos de código difíciles de encontrar./cCompile sin vincular, ya que solo estamos creando la interfaz de módulo con nombre binario en este momento. Puede controlar el nombre del archivo de objeto y el nombre del archivo de interfaz de módulo con los siguientes modificadores:
-
/Foestablece el nombre del archivo de objeto. Por ejemplo:/Fo:"somethingelse". De forma predeterminada, el compilador usa el mismo nombre para el archivo de objeto que el archivo de origen del módulo (.ixx) que está compilando. En el ejemplo, el nombre del archivo de objeto esstd.objde forma predeterminada porque estamos compilando el archivostd.ixxde módulo . -
/ifcOutputestablece el nombre del archivo de interfaz de módulo nombrado (.ifc). Por ejemplo:/ifcOutput "somethingelse.ifc". De forma predeterminada, el compilador usa el mismo nombre para el archivo de interfaz de módulo (.ifc) que el archivo de origen del módulo (.ixx) que está compilando. En el ejemplo, el archivo generadoifcesstd.ifcde forma predeterminada porque estamos compilando el archivostd.ixxde módulo .
-
Importe la
stdbiblioteca que creó creando primero un archivo denominadoimportExample.cppcon el siguiente contenido:// requires /std:c++latest import std; int main() { std::cout << "Import the STL library for best performance\n"; std::vector<int> v{5, 5, 5}; for (const auto& e : v) { std::cout << e; } }En el código anterior,
import std;reemplaza#include <vector>y#include <iostream>. La instrucciónimport std;hace que toda la biblioteca estándar esté disponible con una instrucción . La importación de toda la biblioteca estándar suele ser mucho más rápida que procesar un único archivo de encabezado de biblioteca estándar, como#include <vector>.Compile el ejemplo mediante el comando siguiente en el mismo directorio que el paso anterior:
cl /c /std:c++latest /EHsc /nologo /W4 /reference "std=std.ifc" importExample.cpp link importExample.obj std.objNo es necesario especificar
/reference "std=std.ifc"en la línea de comandos de este ejemplo porque el compilador busca automáticamente el.ifcarchivo que coincide con el nombre del módulo especificado por laimportinstrucción . Cuando el compilador encuentraimport std;, puede encontrarstd.ifcsi se encuentra en el mismo directorio que el código fuente. Si el.ifcarchivo está en un directorio diferente al código fuente, use el/referencemodificador del compilador para hacer referencia a él.En este ejemplo, la compilación del código fuente y la vinculación de la implementación del módulo a la aplicación son pasos independientes. No es necesario que lo sean. Puede usar
cl /std:c++latest /EHsc /nologo /W4 /reference "std=std.ifc" importExample.cpp std.objpara compilar y vincular en un paso. Pero puede ser conveniente compilar y enlazar por separado porque solo necesita compilar la biblioteca estándar denominada módulo una vez y, a continuación, puede hacer referencia a ella desde su proyecto, o desde varios proyectos, en el paso de enlace de la compilación.Si va a compilar un solo proyecto, puede combinar los pasos de creación del módulo con nombre de la
stdbiblioteca estándar y el paso de compilar la aplicación agregando"%VCToolsInstallDir%\modules\std.ixx"a la línea de comandos. Colóquelo antes de los.cpparchivos que consuman elstdmódulo.De forma predeterminada, el nombre del ejecutable de salida se toma del primer archivo de entrada. Use la
/Feopción del compilador para especificar el nombre de archivo ejecutable que desee. En este tutorial se muestra la compilación de un módulo específico como un paso independiente porque solo necesita construir el módulo de biblioteca estándar una vez. Luego, puede referirse a él desde su proyecto o desde varios proyectos. Pero puede ser conveniente compilar todo juntos, como se muestra en esta línea de comandos:cl /FeimportExample /std:c++latest /EHsc /nologo /W4 "%VCToolsInstallDir%\modules\std.ixx" importExample.cppDado la línea de comandos anterior, el compilador genera un archivo ejecutable denominado
importExample.exe. Al ejecutarlo, genera el siguiente resultado:Import the STL library for best performance 555
Importación de la biblioteca estándar y las funciones globales de C con std.compat
La biblioteca estándar de C++ incluye la biblioteca estándar ISO C. El std.compat módulo proporciona toda la funcionalidad del std módulo, como std::vector, std::cout, std::printf, std::scanf, etc. Pero también proporciona las versiones del espacio de nombres global de estas funciones, como ::printf, ::scanf, ::fopen, ::size_t, y así sucesivamente.
El módulo denominado std.compat es una capa de compatibilidad proporcionada para facilitar la migración del código existente que hace referencia a funciones de tiempo de ejecución de C en el espacio de nombres global. Si desea evitar agregar nombres al espacio de nombres global, use import std;. Si necesita facilitar la migración de una base de código que utiliza muchas funciones de tiempo de ejecución de C no calificadas (espacio de nombres global), use import std.compat;. Esto proporciona los nombres del entorno de ejecución C del espacio de nombres global para que no tengas que calificar todos los nombres globales con std::. Si no tiene ningún código existente que use las funciones de tiempo de ejecución del espacio de nombres global de C, no es necesario usar import std.compat;. Si solo llama a algunas funciones en tiempo de ejecución de C en su código, puede ser mejor usar import std; y especificar los pocos nombres del espacio de nombres global de tiempo de ejecución de C que lo requieran con std::. Por ejemplo: std::printf(). Si ve un error similar error C3861: 'printf': identifier not found al intentar compilar el código, considere la posibilidad de usar import std.compat; para importar las funciones en tiempo de ejecución del espacio de nombres global de C.
Ejemplo: Cómo construir e importar std.compat
Para poder usar import std.compat; , debe compilar el archivo de interfaz de módulo que se encuentra en el formulario de código fuente en std.compat.ixx. Visual Studio incluye el código fuente del módulo para que pueda compilar el módulo mediante la configuración del compilador que coincida con el proyecto. Los pasos son similares a los de compilar el módulo con nombre std. El std módulo con nombre se compila primero porque std.compat depende de él:
Abra una consola de herramientas nativas para VS: en el menú Inicio de Windows, escriba x86 nativo y la consola debería aparecer en la lista de aplicaciones. Asegúrese de que la solicitud es para Visual Studio 2022, versión 17.5 o posterior. Si usa la versión incorrecta del indicador, obtendrá errores del compilador.
Cree un directorio para probar este ejemplo, como
%USERPROFILE%\source\repos\STLModulesy convertirlo en el directorio actual. Si elige un directorio al que no tiene acceso de escritura, obtendrá errores.Compile los módulos con nombre
stdystd.compatcon el siguiente comando:cl /std:c++latest /EHsc /nologo /W4 /c "%VCToolsInstallDir%\modules\std.ixx" "%VCToolsInstallDir%\modules\std.compat.ixx"Debe compilar
stdystd.compatusando la misma configuración del compilador que tiene la intención de usar con el código que los importará. Si tiene una solución de varios proyectos, puede compilarlas una vez y, a continuación, hacer referencia a ellas desde todos los proyectos mediante la/referenceopción del compilador.Si recibe errores, asegúrese de que usa la versión correcta de la línea de comandos.
El compilador genera cuatro archivos de los dos pasos anteriores:
-
std.ifces la interfaz de módulo con nombre binario compilado que el compilador consulta para procesar laimport std;instrucción. El compilador también consultastd.ifcpara procesarimport std.compat;porquestd.compatse basa enstd. Este es un artefacto solo para tiempo de compilación. No se incluye con tu aplicación. -
std.objcontiene la implementación de la biblioteca estándar. -
std.compat.ifces la interfaz de módulo con nombre binario compilado que el compilador consulta para procesar laimport std.compat;instrucción. Este es un artefacto exclusivamente durante la compilación. No se envía con la aplicación. -
std.compat.objcontiene la implementación. Sin embargo, la mayoría de la implementación la proporcionastd.obj. Agreguestd.obja la línea de comandos al compilar la aplicación de ejemplo para vincular estáticamente la funcionalidad que se usa desde la biblioteca estándar a la aplicación.
Puede controlar el nombre del archivo de objeto y el nombre del archivo de interfaz de módulo con los siguientes modificadores:
-
/Foestablece el nombre del archivo de objeto. Por ejemplo:/Fo:"somethingelse". De forma predeterminada, el compilador usa el mismo nombre para el archivo de objeto que el archivo de origen del módulo (.ixx) que está compilando. En el ejemplo, los nombres de archivo de objeto sonstd.objystd.compat.objde forma predeterminada porque estamos compilando los archivosstd.ixxde módulo ystd.compat.ixx. -
/ifcOutputestablece el nombre del archivo de interfaz de módulo nombrado (.ifc). Por ejemplo:/ifcOutput "somethingelse.ifc". De forma predeterminada, el compilador usa el mismo nombre para el archivo de interfaz de módulo (.ifc) que el archivo de origen del módulo (.ixx) que está compilando. En el ejemplo, los archivos generadosifcsonstd.ifcystd.compat.ifc, de forma predeterminada, porque estamos compilando los archivosstd.ixxdel módulo ystd.compat.ixx.
-
Importe la
std.compatbiblioteca creando primero un archivo denominadostdCompatExample.cppcon el siguiente contenido:import std.compat; int main() { printf("Import std.compat to get global names like printf()\n"); std::vector<int> v{5, 5, 5}; for (const auto& e : v) { printf("%i", e); } }En el código anterior,
import std.compat;reemplaza#include <cstdio>y#include <vector>. La instrucciónimport std.compat;hace que la biblioteca estándar y las funciones en tiempo de ejecución de C estén disponibles con una instrucción . Importar este módulo con nombre, que incluye la biblioteca estándar de C++ y las funciones del espacio de nombres global de la biblioteca de ejecución de C, es más rápido que procesar solo un#includecomo#include <vector>.Compile el ejemplo mediante el comando siguiente:
cl /std:c++latest /EHsc /nologo /W4 stdCompatExample.cpp link stdCompatExample.obj std.obj std.compat.objNo teníamos que especificar
std.compat.ifcen la línea de comandos porque el compilador busca automáticamente el.ifcarchivo que coincide con el nombre del módulo en unaimportinstrucción . Cuando el compilador encuentraimport std.compat;, detectastd.compat.ifcporque lo colocamos en el mismo directorio que el código fuente, lo que nos evita la necesidad de especificarlo en la línea de comandos. Si el.ifcarchivo está en un directorio diferente al código fuente o tiene un nombre diferente, use el/referencemodificador del compilador para hacer referencia a él.Al importar
std.compat, debe vincular tanto constd.compatcomo constd.obj, porquestd.compatusa código enstd.obj.Si va a compilar un solo proyecto, puede combinar los pasos de compilación de los módulos con nombre de la biblioteca estándar
stdystd.compatagregando"%VCToolsInstallDir%\modules\std.ixx"y"%VCToolsInstallDir%\modules\std.compat.ixx"(en ese orden) a la línea de comandos. En este tutorial se muestra cómo compilar los módulos de biblioteca estándar como un paso independiente, ya que solo necesita compilar la biblioteca estándar denominada módulos una vez y, a continuación, puede hacer referencia a ellos desde el proyecto o desde varios proyectos. Pero si es conveniente compilarlos a la vez, asegúrese de colocarlos antes de los.cpparchivos que los consuman y especifique/Fepara asignar un nombre al compiladoexecomo se muestra en este ejemplo:cl /c /FestdCompatExample /std:c++latest /EHsc /nologo /W4 "%VCToolsInstallDir%\modules\std.ixx" "%VCToolsInstallDir%\modules\std.compat.ixx" stdCompatExample.cpp link stdCompatExample.obj std.obj std.compat.objEn este ejemplo, la compilación del código fuente y la vinculación de la implementación del módulo a la aplicación son pasos independientes. No es necesario que lo sean. Puede usar
cl /std:c++latest /EHsc /nologo /W4 stdCompatExample.cpp std.obj std.compat.objpara compilar y vincular en un paso. Sin embargo, puede ser conveniente compilar y enlazar por separado porque así solo necesitará compilar los módulos de la biblioteca estándar una vez y luego podrá referirse a ellos desde su proyecto o desde múltiples proyectos en el paso de enlace de su compilación.El comando del compilador anterior genera un archivo ejecutable denominado
stdCompatExample.exe. Al ejecutarlo, genera el siguiente resultado:Import std.compat to get global names like printf() 555
Consideraciones sobre módulos con nombre de biblioteca estándar
El control de versiones para los módulos con nombre es el mismo que para los encabezados. Los archivos de módulo denominados .ixx se instalan junto con los encabezados, por ejemplo: "%VCToolsInstallDir%\modules\std.ixx", que se resuelven a C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.38.33130\modules\std.ixx en la versión de las herramientas utilizadas al momento de redactar este documento. Seleccione la versión del módulo nombrado de la misma manera que elige la versión del archivo de encabezado: por el directorio desde el cual los referencia.
No mezcles la importación de archivos de encabezado y módulos nombrados. Por ejemplo, no import <vector>; y import std; en el mismo archivo.
No mezcle ni combine la importación de archivos de encabezado de la biblioteca estándar de C++ y los módulos llamados std o std.compat. Por ejemplo, no #include <vector> y import std; en el mismo archivo. Sin embargo, puede incluir encabezados de C e importar módulos con nombre en el mismo archivo. Por ejemplo, puede realizar import std; y #include <math.h> en el mismo archivo. Simplemente no incluya la versión <cmath>de la biblioteca estándar de C++ .
No tiene que preocuparse por importar un módulo más de una vez. Es decir, no necesita protectores de encabezado de estilo #ifndef en los módulos. El compilador sabe cuándo ya ha importado un módulo con nombre y omite los intentos duplicados de hacerlo.
Si necesita usar la macro assert(), entonces #include <assert.h>.
Si necesita usar la errno macro , #include <errno.h>. Dado que los módulos con nombre no exponen macros, esta es la solución alternativa si necesita comprobar si hay errores de <math.h>, por ejemplo.
Las macros como NAN, INFINITYy INT_MIN se definen mediante <limits.h>, que puede incluir. Sin embargo, si import std;, puede usar std::numeric_limits<double>::quiet_NaN() y std::numeric_limits<double>::infinity() en lugar de NAN y INFINITY, y std::numeric_limits<int>::min() en lugar de INT_MIN.
Resumen
En este tutorial, ha importado la biblioteca estándar mediante módulos. A continuación, obtenga información sobre cómo crear e importar sus propios módulos en el tutorial Módulos con nombre en C++.
Consulte también
Comparación de unidades de encabezado, módulos y encabezados precompilados
Introducción a los módulos en C++
Un paseo por módulos de C++ en Visual Studio
Mover un proyecto a C++ denominado Modules