Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В 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-seq
identifier
.
module-partition
:
:
module-name
module-declaration
:
export
выбратьmodule
module-name
module-partition
выбратьattribute-specifier-seq
выбрать;
module-import-declaration
:
export
выбратьimport
module-name
attribute-specifier-seq
выбрать;
export
выбратьimport
module-partition
attribute-specifier-seq
выбрать;
export
выбратьimport
header-name
attribute-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
, import
, export
Руководство по именованным модулям
Сравнение единиц заголовков, модулей и предварительно скомпилированных заголовков