Руководство. Импорт стандартной библиотеки C++ с помощью модулей из командной строки
Узнайте, как импортировать стандартную библиотеку C++ с помощью модулей библиотекИ C++. Это приводит к более быстрой компиляции и более надежной, чем использование файлов заголовков или блоков заголовков или предварительно скомпилированных заголовков (PCH).
В этом руководстве описано:
- Как импортировать стандартную библиотеку в виде модуля из командной строки.
- Преимущества производительности и удобства использования модулей.
- Два стандартных модуля библиотеки
std
иstd.compat
разница между ними.
Необходимые компоненты
Для работы с этим руководством требуется Visual Studio 2022 17.5 или более поздней версии.
Общие сведения о модулях стандартной библиотеки
Файлы заголовков страдают от семантики, которая может изменяться в зависимости от определений макросов, порядка их включения и медленной компиляции. Модули решают эти проблемы.
Теперь можно импортировать стандартную библиотеку в виде модуля вместо танга файлов заголовков. Это гораздо быстрее и надежнее, чем в том числе файлы заголовков или блоки заголовков или предварительно скомпилированные заголовки (PCH).
Стандартная библиотека C++23 содержит два именованных модуля: std
и std.compat
:
std
экспортирует объявления и имена, определенные в стандартном пространствеstd
имен библиотеки C++, напримерstd::vector
. Он также экспортирует содержимое заголовков оболочки C, таких как<cstdio>
и<cstdlib>
, которые предоставляют такие функции, какstd::printf()
. Функции C, определенные в глобальном пространстве имен, например::printf()
, не экспортируются. Это улучшает ситуацию, когда в том числе заголовок оболочки C, например<cstdio>
stdio.h
, содержит такие файлы заголовков C, как и в глобальных версиях пространства имен C. Это не проблема при импортеstd
.std.compat
экспортирует все иstd
добавляет глобальные пространства имен среды выполнения C, такие как::printf
,::fopen
,::size_t
и::strlen
т. д. Модульstd.compat
упрощает работу с базами кода, которые ссылаются на многие функции и типы среды выполнения C в глобальном пространстве имен.
Компилятор импортирует всю стандартную библиотеку при использовании import std;
или import std.compat;
выполняет ее быстрее, чем при вводе в один файл заголовка. Это быстрее, чтобы привести всю стандартную библиотеку с import std;
(или import std.compat
) к #include <vector>
, например.
Так как именованные модули не предоставляют макросы, макросы, например assert
, errno
, offsetof
и va_arg
другие, недоступны при импорте std
или std.compat
. Рекомендации по обходным решениям см. в стандартной библиотеке именованных модулей.
Сведения о модулях C++
Файлы заголовков — это способ совместного использования объявлений и определений между исходными файлами в C++. Перед модулями стандартной библиотеки вы будете включать часть стандартной библиотеки, необходимую для директивы, например #include <vector>
. Файлы заголовков являются хрупкими и сложными, так как их семантика может изменяться в зависимости от порядка их включения или определения определенных макросов. Они также медленно компилируются, так как они повторно обрабатывается каждым исходным файлом, который включает их.
В C++20 представлен современный альтернативный вариант, называемый модулями. В C++23 мы смогли использовать поддержку модулей, чтобы представить именованные модули для представления стандартной библиотеки.
Как и файлы заголовков, модули позволяют совместно использовать объявления и определения в исходных файлах. Но в отличие от файлов заголовков модули не хрупки и проще создавать их, так как их семантика не изменяется из-за определений макросов или порядка их импорта. Компилятор может обрабатывать модули гораздо быстрее, чем может обрабатывать #include
файлы, а также использовать меньше памяти во время компиляции. Именованные модули не предоставляют определения макросов или сведения о частной реализации.
Подробные сведения о модулях см. в разделе "Обзор модулей в C++ В этой статье также рассматривается использование стандартной библиотеки C++ в качестве модулей, но используется более старый и экспериментальный способ его выполнения".
В этой статье демонстрируется новый и лучший способ использования стандартной библиотеки. Дополнительные сведения о альтернативных способах использования стандартной библиотеки см. в статье "Сравнение единиц заголовков, модулей и предварительно скомпилированных заголовков".
Импорт стандартной библиотеки с помощью std
В следующих примерах показано, как использовать стандартную библиотеку в качестве модуля с помощью компилятора командной строки. Сведения о том, как это сделать в интегрированной среде разработки Visual Studio, см. в разделе "Сборка модулей стандартной библиотеки ISO C++23".
Инструкция import std;
или import std.compat;
импорт стандартной библиотеки в приложение. Но сначала необходимо скомпилировать стандартные именованные модули библиотеки в двоичную форму. Ниже показано, как это сделать.
Пример. Создание и импорт std
Откройте командную строку собственных средств x86 для VS: в меню "Пуск" Windows введите собственный код x86, а в списке приложений должен появиться запрос. Убедитесь, что запрос предназначен для Visual Studio 2022 версии 17.5 или выше. Если вы используете неправильную версию запроса, вы получите ошибки. Примеры, используемые в этом руководстве, предназначены для оболочки CMD.
Создайте каталог, например
%USERPROFILE%\source\repos\STLModules
и сделайте его текущим каталогом. Если у вас нет доступа на запись, вы получите ошибки во время компиляции.Скомпилируйте именованный модуль с помощью следующей
std
команды:cl /std:c++latest /EHsc /nologo /W4 /c "%VCToolsInstallDir%\modules\std.ixx"
Если возникают ошибки, убедитесь, что вы используете правильную версию командной строки.
Скомпилируйте именованный
std
модуль, используя те же параметры компилятора, которые вы планируете использовать с кодом, импортируемым встроенным модулем. Если у вас есть решение с несколькими проектами, можно скомпилировать стандартную библиотеку с именем модуля один раз, а затем обратиться к нему из всех проектов с помощью параметра компилятора/reference
.Используя предыдущую команду компилятора, компилятор выводит два файла:
std.ifc
— это скомпилированное двоичное представление именованного интерфейса модуля, который компилятор обращается к процессу инструкцииimport std;
. Это артефакт только во время компиляции. Он не отправляется с приложением.std.obj
содержит реализацию именованного модуля. Добавьтеstd.obj
в командную строку при компиляции примера приложения для статического связывания функциональных возможностей, используемых из стандартной библиотеки в приложение.
Параметры командной строки в этом примере:
Коммутатор Значение /std:c++:latest
Используйте последнюю версию стандарта языка C++ и библиотеки. Хотя поддержка модулей доступна в разделе /std:c++20
, вам нужна последняя стандартная библиотека, чтобы получить поддержку стандартных именованных модулей библиотеки./EHsc
Используйте обработку исключений C++ за исключением функций, помеченных extern "C"
./W4
Использование /W4 обычно рекомендуется, особенно для новых проектов, так как он включает все уровни 1, уровень 2, уровень 3 и большинство предупреждений уровня 4 (информационные), которые могут помочь вам поймать потенциальные проблемы рано. Он, по сути, предоставляет предупреждения, подобные lint, которые могут помочь обеспечить наименьшие возможные дефекты кода. /c
Скомпилируйте без связывания, так как мы просто создадим двоичный именованный интерфейс модуля на этом этапе. Вы можете управлять именем файла объекта и именем именованного файла интерфейса модуля с помощью следующих переключателей:
/Fo
задает имя файла объекта. Например,/Fo:"somethingelse"
. По умолчанию компилятор использует то же имя для файла объекта, что и исходный файл модуля (.ixx
). В примере имя файла объекта по умолчанию являетсяstd.obj
тем, что мы компилируем файлstd.ixx
модуля./ifcOutput
задает имя именованного файла интерфейса модуля (.ifc
). Например,/ifcOutput "somethingelse.ifc"
. По умолчанию компилятор использует то же имя для файла интерфейса модуля (.ifc
), что и исходный файл модуля (.ixx
). В примере созданныйifc
файл по умолчанию являетсяstd.ifc
по умолчанию, так как мы компилируем файлstd.ixx
модуля.
Импортируйте созданную библиотеку
std
, сначала создав файл с именемimportExample.cpp
со следующим содержимым:// 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; } }
В приведенном выше коде
import std;
#include <vector>
заменяет и#include <iostream>
.import std;
Оператор делает все стандартные библиотеки доступными с одной инструкцией. Импорт всей стандартной библиотеки часто гораздо быстрее, чем обработка одного файла заголовка стандартной библиотеки, например#include <vector>
.Скомпилируйте пример с помощью следующей команды в том же каталоге, что и предыдущий шаг:
cl /c /std:c++latest /EHsc /nologo /W4 /reference "std=std.ifc" importExample.cpp link importExample.obj std.obj
В этом примере не требуется указывать
/reference "std=std.ifc"
в командной строке, так как компилятор автоматически ищет.ifc
файл, соответствующий имени модуля, заданному инструкциейimport
. Когда компилятор обнаруживаетimport std;
его, может ли он находитьсяstd.ifc
в том же каталоге, что и исходный код..ifc
Если файл находится в каталоге, отличном от исходного кода, используйте параметр компилятора/reference
для ссылки на него.В этом примере компиляция исходного кода и связывание реализации модуля с приложением являются отдельными шагами. Они не должны быть. Вы можете использовать
cl /std:c++latest /EHsc /nologo /W4 /reference "std=std.ifc" importExample.cpp std.obj
для компиляции и связывания на одном шаге. Но это может быть удобно для сборки и связывания отдельно, так как затем необходимо создать стандартную библиотеку с именем модуля один раз, а затем вы можете ссылаться на него из проекта или из нескольких проектов на шаге ссылки сборки.Если вы создаете один проект, вы можете объединить шаги по созданию
std
стандартной библиотеки с именем модуля и этапу создания приложения, добавив"%VCToolsInstallDir%\modules\std.ixx"
в командную строку. Поместите его перед любыми.cpp
файлами, которые используютstd
модуль.По умолчанию имя выходного исполняемого файла берется из первого входного файла. Используйте параметр компилятора, чтобы указать нужное
/Fe
имя исполняемого файла. В этом руководстве показано, как скомпилировать именованныйstd
модуль как отдельный шаг, так как необходимо создать стандартную библиотеку с именем модуля один раз, а затем можно ссылаться на него из проекта или из нескольких проектов. Но может быть удобно создать все вместе, как показано в этой командной строке:cl /FeimportExample /std:c++latest /EHsc /nologo /W4 "%VCToolsInstallDir%\modules\std.ixx" importExample.cpp
Учитывая предыдущую командную строку, компилятор создает исполняемый файл с именем
importExample.exe
. При запуске он создает следующие выходные данные:Import the STL library for best performance 555
Импорт стандартной библиотеки и глобальных функций C с помощью std.compat
Стандартная библиотека C++ включает стандартную библиотеку ISO C. Модуль std.compat
предоставляет все функциональные возможности std
модуля, например std::vector
, std::cout
, std::printf
и std::scanf
т. д. Но она также предоставляет версии глобального пространства имен таких функций, как ::printf
, ::scanf
, ::fopen
и ::size_t
т. д.
Именованный std.compat
модуль — это уровень совместимости, предоставляемый для упрощения переноса существующего кода, который относится к функциям среды выполнения C в глобальном пространстве имен. Если вы хотите избежать добавления имен в глобальное пространство имен, используйте import std;
. Если вам нужно упростить миграцию базы кода, использующую множество неквалифицированных (глобальное пространство имен) функций среды выполнения C, используйте import std.compat;
. Это обеспечивает имена среды выполнения C глобального пространства имен, чтобы вам не нужно было указывать все глобальные имена.std::
Если у вас нет существующего кода, использующего функции среды выполнения C глобального пространства имен, вам не нужно использовать import std.compat;
. Если вы вызываете только несколько функций среды выполнения C в коде, возможно, лучше использовать import std;
и указать несколько имен глобального пространства имен C, которым он std::
нужен. Например, std::printf()
. Если при попытке компиляции кода возникает ошибка error C3861: 'printf': identifier not found
, рассмотрите возможность import std.compat;
импорта функций среды выполнения C глобального пространства имен.
Пример. Создание и импорт std.compat
Прежде чем использовать import std.compat;
, необходимо скомпилировать файл интерфейса модуля, найденный в форме std.compat.ixx
исходного кода. Visual Studio поставляет исходный код модуля, чтобы можно было скомпилировать модуль с помощью параметров компилятора, соответствующих проекту. Действия аналогичны созданию именованного std
модуля. Именованный std
модуль создается сначала, так как std.compat
зависит от него:
Откройте командную строку собственных средств для VS: в меню "Пуск" Windows введите собственный код x86 и в списке приложений должна появиться строка. Убедитесь, что запрос предназначен для Visual Studio 2022 версии 17.5 или выше. Если вы используете неправильную версию запроса, вы получите ошибки компилятора.
Создайте каталог, чтобы попробовать этот пример, например
%USERPROFILE%\source\repos\STLModules
, и сделать его текущим каталогом. Если вы выберете каталог, в который у вас нет доступа на запись, вы получите ошибки.std
Скомпилируйте иstd.compat
именованные модули с помощью следующей команды:cl /std:c++latest /EHsc /nologo /W4 /c "%VCToolsInstallDir%\modules\std.ixx" "%VCToolsInstallDir%\modules\std.compat.ixx"
Необходимо скомпилировать
std
иstd.compat
использовать те же параметры компилятора, которые вы планируете использовать с кодом, который будет импортировать их. Если у вас есть решение с несколькими проектами, их можно скомпилировать один раз, а затем ссылаться на них из всех проектов с помощью параметра компилятора/reference
.Если возникают ошибки, убедитесь, что вы используете правильную версию командной строки.
Компилятор выводит четыре файла из предыдущих двух шагов:
std.ifc
— это скомпилированный двоичный именованный интерфейс модуля, который компилятор обращается к процессу инструкцииimport std;
. Компилятор также обращается кstd.ifc
процессу, так какstd.compat
выполняет сборкуstd
import std.compat;
. Это артефакт только во время компиляции. Он не отправляется с приложением.std.obj
содержит реализацию стандартной библиотеки.std.compat.ifc
— это скомпилированный двоичный именованный интерфейс модуля, который компилятор обращается к процессу инструкцииimport std.compat;
. Это артефакт только во время компиляции. Он не отправляется с приложением.std.compat.obj
содержит реализацию. Однако большая часть реализации предоставляетсяstd.obj
. Добавьтеstd.obj
в командную строку при компиляции примера приложения для статического связывания функциональных возможностей, используемых из стандартной библиотеки в приложение.
Вы можете управлять именем файла объекта и именем именованного файла интерфейса модуля с помощью следующих переключателей:
/Fo
задает имя файла объекта. Например,/Fo:"somethingelse"
. По умолчанию компилятор использует то же имя для файла объекта, что и исходный файл модуля (.ixx
). В этом примере имена файлов объектов и по умолчанию являютсяstd.obj
иstd.compat.obj
по умолчанию, так как компилируем файлыstd.ixx
модуля иstd.compat.obj
./ifcOutput
задает имя именованного файла интерфейса модуля (.ifc
). Например,/ifcOutput "somethingelse.ifc"
. По умолчанию компилятор использует то же имя для файла интерфейса модуля (.ifc
), что и исходный файл модуля (.ixx
). В этом примере созданныеifc
файлы по умолчанию являютсяstd.ifc
иstd.compat.ifc
по умолчанию, так как мы компилируем файлыstd.ixx
модуля иstd.compat.ixx
.
Импортируйте библиотеку
std.compat
, сначала создав файл с именемstdCompatExample.cpp
со следующим содержимым: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); } }
В приведенном выше коде
import std.compat;
#include <cstdio>
заменяет и#include <vector>
.import std.compat;
Инструкция делает стандартную библиотеку и функции среды выполнения C доступными с помощью одной инструкции. Импорт этого именованного модуля, который включает стандартную библиотеку C++ и функции глобального пространства имен среды выполнения C, выполняется быстрее, чем обработка одного#include
, например#include <vector>
.Скомпилируйте пример с помощью следующей команды:
cl /std:c++latest /EHsc /nologo /W4 stdCompatExample.cpp link stdCompatExample.obj std.obj std.compat.obj
Нам не нужно указывать
std.compat.ifc
в командной строке, так как компилятор автоматически ищет.ifc
файл, соответствующий имени модуля в инструкцииimport
. Когда компилятор обнаруживаетimport std.compat;
егоstd.compat.ifc
, так как мы помещаем его в тот же каталог, что и исходный код, который позволяет нам указать его в командной строке..ifc
Если файл находится в каталоге, отличном от исходного кода или имеет другое имя, используйте/reference
переключатель компилятора для ссылки на него.При импорте
std.compat
необходимо связаться с обоимиstd.compat
иstd.obj
потому, чтоstd.compat
использует код вstd.obj
.Если вы создаете один проект, вы можете объединить шаги по созданию
std
именованных модулей иstd.compat
стандартной библиотеки, добавив"%VCToolsInstallDir%\modules\std.ixx"
и"%VCToolsInstallDir%\modules\std.compat.ixx"
(в этом порядке) в командную строку. В этом руководстве показано создание модулей стандартной библиотеки как отдельный шаг, так как необходимо создать только стандартные именованные модули библиотеки один раз, а затем можно ссылаться на них из проекта или из нескольких проектов. Но если их удобно создавать одновременно, обязательно поместите их перед любыми.cpp
файлами, которые используют их, и укажите/Fe
имя встроенного,exe
как показано в этом примере: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.obj
В этом примере компиляция исходного кода и связывание реализации модуля с приложением являются отдельными шагами. Они не должны быть. Вы можете использовать
cl /std:c++latest /EHsc /nologo /W4 stdCompatExample.cpp std.obj std.compat.obj
для компиляции и связывания на одном шаге. Но это может быть удобно для сборки и связывания отдельно, так как затем необходимо создать стандартные библиотеки именованных модулей один раз, а затем вы можете ссылаться на них из проекта или из нескольких проектов на шаге ссылки сборки.Предыдущая команда компилятора создает исполняемый файл с именем
stdCompatExample.exe
. При запуске он создает следующие выходные данные:Import std.compat to get global names like printf() 555
Рекомендации по использованию стандартной библиотеки с именем модуля
Управление версиями именованных модулей совпадает с заголовками. Файлы .ixx
именованного модуля устанавливаются вместе с заголовками, например "%VCToolsInstallDir%\modules\std.ixx
: который разрешается C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.38.33130\modules\std.ixx
в версию средств, используемых во время записи. Выберите версию именованного модуля так же, как и версию файла заголовка для использования каталогом, который вы ссылаетесь на них.
Не смешивайте и не сопоставляйте блоки заголовков и именованные модули. Например, не import <vector>;
import std;
и в одном файле.
Не смешивайте и не сопоставляйте файлы заголовков стандартной библиотеки C++ и именованные модули std
или std.compat
. Например, не #include <vector>
import std;
и в одном файле. Однако вы можете включить заголовки C и импортировать именованные модули в один и тот же файл. Например, можно import std;
и #include <math.h>
в одном файле. Просто не включайте стандартную версию <cmath>
библиотеки C++ .
Вам не нужно защищаться от импорта модуля несколько раз. Т. е. в модулях не требуются #ifndef
охранники заголовков стилей. Компилятор знает, когда он уже импортировал именованный модуль и игнорирует повторяющиеся попытки сделать это.
Если необходимо использовать assert()
макрос, то #include <assert.h>
.
Если необходимо использовать errno
макрос, #include <errno.h>
. Так как именованные модули не предоставляют макросы, это обходное решение, если необходимо проверка для ошибок<math.h>
, например.
Макросы, такие как NAN
INFINITY
, и INT_MIN
определяются с помощью <limits.h>
которых можно включить. Однако, если вы import std;
можете использовать numeric_limits<double>::quiet_NaN()
и numeric_limits<double>::infinity()
вместо NAN
INFINITY
него, а std::numeric_limits<int>::min()
не INT_MIN
.
Итоги
В этом руководстве вы импортировали стандартную библиотеку с помощью модулей. Далее вы узнаете о создании и импорте собственных модулей в учебнике по именованным модулям в C++.
См. также
Сравнение единиц заголовков, модулей и предварительно скомпилированных заголовков
Обзор модулей в C++
Обзор модулей C++ в Visual Studio
Перемещение проекта в модули C++ с именем
Обратная связь
https://aka.ms/ContentUserFeedback.
Ожидается в ближайшее время: в течение 2024 года мы постепенно откажемся от GitHub Issues как механизма обратной связи для контента и заменим его новой системой обратной связи. Дополнительные сведения см. в разделеОтправить и просмотреть отзыв по