Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В C++20 представлены модули . Модуль — это набор файлов исходного кода, которые компилируются независимо от исходных файлов (или более точно, единиц перевода), импортируемых ими.
Модули устраняют или сокращают многие проблемы, связанные с использованием файлов заголовков. Они часто сокращают время компиляции, иногда значительно. Макросы, директивы препроцессора и неэкспортированные имена, объявленные в модуле, не отображаются за пределами модуля. Они не влияют на компиляцию единицы перевода, импортируемой модулем. Модули можно импортировать в любом порядке без проблем с переопределениями макросов. Объявления в импортирующей единице перевода не участвуют в разрешении перегрузки или поиске имен в импортированном модуле. После компиляции модуля результаты хранятся в двоичном файле, который описывает все экспортированные типы, функции и шаблоны. Компилятор может обрабатывать этот файл гораздо быстрее, чем файл заголовка. И компилятор может повторно использовать его везде, где модуль импортируется в проект.
Модули можно использовать параллельно с файлами заголовков. Исходный файл C++ может содержать import модули, а также #include файлы заголовков. В некоторых случаях файл заголовка можно импортировать в виде модуля, который быстрее, чем использование #include для обработки с помощью препроцессора. Рекомендуем по возможности использовать модули в новых проектах, а не файлы заголовков. Для более крупных существующих проектов при активной разработке поэкспериментируйте с преобразованием устаревших заголовков в модули. Основывайте внедрение на том, достигается ли значительное сокращение времени компиляции.
Сведения о контрастировании модулей с другими способами импорта стандартной библиотеки см. в разделе Сравнение единиц заголовков, модулей и предварительно скомпилированных заголовков.
Начиная с Visual Studio 2022 версии 17.5 импорт стандартной библиотеки в качестве модуля является стандартизованным и полностью реализован в компиляторе Microsoft C++. Сведения о импорте стандартной библиотеки с помощью модулей см. в статье Импорт стандартной библиотеки C++ с помощью модулей.
Модули с одним разделом
Модуль с одним разделом — это модуль, состоящий из одного исходного файла. Интерфейс модуля и реализация находятся в одном файле.
В следующем примере модуля с одним разделом показан простое определение модуля в исходном файле с именем Example.ixx. Расширение .ixx — это расширение по умолчанию для файлов интерфейса модуля в Visual Studio. Если вы хотите использовать другое расширение, используйте параметр /interface для компиляции в виде интерфейса модуля. В этом примере файл интерфейса содержит определение функции и объявление. Вы также можете поместить определения в один или несколько отдельных файлов реализации модуля, как показано в следующем примере, но это пример односекционного модуля.
Оператор export module Example; указывает, что этот файл является основным интерфейсом для модуля с именем Example. Модификатор export перед int f() указывает, что эта функция видна, когда другая программа или модуль импортирует Example:
// Example.ixx
export module Example;
#define ANSWER 42
namespace Example_NS
{
int f_internal()
{
return ANSWER;
}
export int f()
{
return f_internal();
}
}
Файл MyProgram.cpp использует import для доступа к имени, экспортируемого Example. Имя пространства имен Example_NS отображается здесь, но не все его члены, так как они не экспортируются. Кроме того, макрос ANSWER не отображается, так как макросы не экспортируются.
// MyProgram.cpp
import std;
import Example;
using namespace std;
int main()
{
cout << "The result of f() is " << Example_NS::f() << endl; // 42
// int i = Example_NS::f_internal(); // C2039
// int j = ANSWER; //C2065
}
Объявление import может отображаться только в глобальной области. Модуль и код, который использует его, необходимо скомпилировать с теми же параметрами компилятора.
Грамматика модуля
module-name:
module-name-qualifier-seqвыбиратьidentifier
module-name-qualifier-seq:
identifier.
module-name-qualifier-seqidentifier.
module-partition:
:module-name
module-declaration:
exportвыбратьmodulemodule-namemodule-partitionвыбратьattribute-specifier-seqвыбрать;
module-import-declaration:
exportвыбратьimportmodule-nameattribute-specifier-seqвыбрать;
exportвыбратьimportmodule-partitionattribute-specifier-seqвыбрать;
exportвыбратьimportheader-nameattribute-specifier-seqвыбрать;
Реализация модулей
Интерфейс модуля экспортирует имя модуля и все пространства имен, типы, функции и т. д., составляющие общедоступный интерфейс модуля.
Реализация модуля определяет вещи, экспортированные модулем.
В самой простой форме модуль может быть одним файлом, который объединяет интерфейс модуля и реализацию. Вы также можете поместить реализацию в один или несколько отдельных файлов реализации модуля, как это делают файлы .h и .cpp.
Для более крупных модулей можно разделить части модуля на подмодулы, называемые секциями. Каждая секция состоит из файла интерфейса модуля, который экспортирует имя секции модуля. У раздела также может быть один или несколько файлов реализации раздела. Модуль в целом имеет один основной интерфейс модуля, который является общедоступным интерфейсом модуля. При необходимости он может экспортировать интерфейсы секций.
Модуль состоит из одного или нескольких единиц модуля . Модуль — это единица перевода (исходный файл), содержащая объявление модуля. Существует несколько типов единиц модуля:
- Модуль интерфейса модуля экспортирует имя модуля или имя секции модуля. Интерфейсная единица модуля имеет
export moduleв своем объявлении. - Модуль реализации модуля не экспортирует имя модуля или имя секции модуля. Как подразумевает имя, он реализует модуль.
- Блок интерфейса основного модуля экспортирует имя модуля. В модуле должен быть один и только один основной блок интерфейса.
- Блок интерфейса секции модуля экспортирует имя секции модуля.
-
блок реализации раздела модуля имеет имя раздела модуля в его объявлении, но отсутствует ключевое слово
export.
Ключевое слово export используется только в файлах интерфейса. Файл реализации может import другом модуле, но он не может export каких-либо имен. Файлы реализации могут иметь любое расширение.
Модули, пространства имен и зависимый от аргументов поиск
Правила для пространств имен в модулях совпадают с любым другим кодом. Если объявление в пространстве имен экспортируется, то включающее пространство имен (за исключением элементов, которые не экспортируются явным образом в этом пространстве имен), также экспортируется неявно. Если пространство имен экспортируется явно, экспортируются все объявления в определении пространства имен.
Когда компилятор выполняет поиск, зависящий от аргументов, для разрешения перегрузки в импортирующей единице перевода, он рассматривает функции, объявленные в той же единице перевода (включая интерфейсы модулей), где определен тип аргументов функции.
Разделы модулей
Раздел модуля аналогичен модулю, за исключением следующих:
- Разделяет права на все объявления во всем модуле.
- Все имена, экспортированные файлами интерфейса секционирования, импортируются и экспортируются основным файлом интерфейса.
- Имя раздела должно начинаться с имени модуля, за которым следует двоеточие (
:). - Объявления в любом из разделов отображаются во всем модуле.
- Для предотвращения ошибок правила одного определения (ODR) не требуются специальные меры предосторожности. Можно объявить имя (функция, класс и т. д.) в одной секции и определить его в другом.
Файл реализации секции начинается следующим образом и является внутренней секцией с точки зрения стандартов C++:
module Example:part1;
Файл интерфейса раздела начинается следующим образом:
export module Example:part1;
Чтобы получить доступ к объявлениям в другом разделе, необходимо импортировать их. Но он может использовать только имя секции, а не имя модуля:
module Example:part2;
import :part1;
Блок первичного интерфейса должен импортировать и повторно экспортировать все файлы секционирования интерфейса модуля, как показано ниже.
export import :part1;
export import :part2;
Основной блок интерфейса может импортировать файлы реализации секций, но не может экспортировать их. Этим файлам запрещено экспортировать любые имена. Это ограничение позволяет модулю хранить сведения о реализации внутри модуля.
Модули и файлы заголовков
Файлы заголовков можно включить в исходный файл модуля, поместив директиву #include перед объявлением модуля. Эти файлы считаются частью глобального фрагмента модуля . Модуль может видеть только имена в фрагменте глобального модуля, которые находятся в заголовках, которые он явно включает. Глобальный фрагмент модуля содержит только символы, используемые.
// MyModuleA.cpp
#include "customlib.h"
#include "anotherlib.h"
import std;
import MyModuleB;
//... rest of file
Для управления импортом модулей можно использовать традиционный файл заголовка:
// MyProgram.h
#ifdef C_RUNTIME_GLOBALS
import std.compat;
#else
import std;
#endif
Импортированные файлы заголовков
Некоторые заголовки настолько автономны, что их можно использовать с помощью ключевого слова import. Основное различие между импортированным заголовком и импортированным модулем заключается в том, что все определения препроцессора в заголовке отображаются в программе импорта сразу после инструкции import.
import <vector>;
import "myheader.h";
См. также
Импорт стандартной библиотеки C++ с помощью модулей
module, , importexport
Руководство по именованным модулям
Сравнение единиц заголовков, модулей и предварительно скомпилированных заголовков