Пошаговое руководство: Импорт библиотек STL как заголовочных единиц

В этом пошаговом руководстве показано, как импортировать библиотеки стандартной библиотеки шаблонов C++ (STL) в качестве единиц заголовков в Visual Studio. Более быстрый и надежный способ импорта стандартной библиотеки см. в руководстве по импорту стандартной библиотеки C++ с помощью модулей.

Импорт заголовка STL в виде единицы заголовка проще, чем использование предварительно скомпилированных файлов заголовков. Модули заголовков проще настроить и использовать, занимают значительно меньше места на диске, обеспечивают сопоставимые улучшения производительности и являются более гибкими, чем общий PCH.

Дополнительные сведения о единицах заголовков и преимуществах, которые они предоставляют, см. в разделе "Что такое единица заголовка?". Чтобы контрастировать блоки заголовков с другими способами импорта стандартной библиотеки, см. раздел "Сравнение единиц заголовков", модулей и предварительно скомпилированных заголовков.

Предварительные условия

Чтобы использовать блоки заголовков, используйте Visual Studio 2022 или более поздней версии или Visual Studio 2019 версии 16.11 или более поздней. Опция /std:c++20 (или более поздняя) необходима для использования заголовочных единиц.

Два подхода к импорту заголовков STL в виде единиц заголовков

Перед импортом заголовка STL его необходимо скомпилировать в модуль заголовка. Блок заголовка — это двоичное представление файла заголовка. Он имеет .ifc расширение.

Рекомендуется создать статическую библиотеку, содержащую встроенные блоки заголовков для заголовков STL, которые вы хотите использовать. Затем наведите ссылку на библиотеку и import его блоки заголовков. Такой подход может привести к более быстрым сборкам и более эффективному использованию. Сведения об этом подходе см. в разделе «Подход 1: Создание статической библиотеки из единиц заголовков библиотеки STL».

Другой подход заключается в том, чтобы Visual Studio сканировал заголовки STL, которые вы #include в проекте, компилировали их в единицы заголовков, а import не #include эти заголовки. Этот подход полезен, если у вас есть большая база кода, так как вам не нужно изменять исходный код. Этот подход менее гибкий, чем подход статической библиотеки, так как он не позволяет повторно использовать встроенные блоки заголовков в других проектах. Но вы по-прежнему получаете преимущество производительности импорта отдельных библиотек STL как заголовочных единиц. Чтобы протестировать этот подход, см. раздел «Подход 2: Сканирование файлов заголовков STL для импорта».

Подход 1. Создание статической библиотеки единиц заголовка библиотеки STL

Рекомендуемый способ использовать библиотеки STL в качестве единиц заголовка — создать один или несколько проектов статической библиотеки. Эти проекты должны состоять из единиц заголовков библиотеки STL, которые вы хотите использовать. Затем добавьте ссылку на проекты библиотеки, чтобы использовать эти заголовочные единицы STL. Это похоже на использование общих предварительно скомпилированных заголовков, но проще.

Блоки заголовков (и модули), встроенные в проект статической библиотеки, автоматически доступны для проектов, ссылающихся, так как система проектов автоматически добавляет соответствующий /headerUnit параметр командной строки компилятору, чтобы проекты, ссылающиеся, могли импортировать блоки заголовков.

Этот подход гарантирует, что блок заголовка для определенного заголовка создается только один раз. Он позволяет импортировать некоторые или все блоки заголовков, которые невозможно использовать с PCH. Единицы заголовков можно включить в любом порядке.

В следующем примере создается проект статической библиотеки, состоящий из единиц заголовка <iostream> и <vector>. После сборки решения вы сошлетесь на этот проект общего блока заголовка из другого проекта C++. Везде, где бы ни было найдено import <iostream>; или import <vector>;, используется собранная единица заголовка для этой библиотеки вместо преобразования заголовка с препроцессором. Это улучшает производительность сборки, как это делают PCH-файлы, когда один и тот же заголовок включен в несколько файлов. Заголовок не должен многократно обрабатываться файлами, содержащими его. Вместо этого уже обработанный скомпилированный блок заголовка импортируется.

Чтобы создать статическую библиотеку, содержащую STL-библиотеки <iostream> и <vector>, выполните следующие действия.

  1. Создайте пустой проект C++. Назовите его SharedPrj.
    Выберите пустой проект для C++ из типов проектов, доступных в окне создания проекта : Снимок экрана: создание пустого проекта C++

  2. Добавьте новый файл C++ в проект. Измените содержимое файла на:

    import <iostream>;
    import <vector>;
    

Настройка свойств проекта

Задайте свойства проекта для совместного использования блоков заголовка из этого проекта:

  1. В главном меню Visual Studio выберите Project> "Свойства SharedPrj", чтобы открыть диалоговое окно страниц свойств проекта:Снимок экрана, отображающий параметры типа конфигурации и стандарта языка C++.
  2. Выберите все конфигурации в раскрывающемся списке "Конфигурация" и выберите "Все платформы" в раскрывающемся списке "Платформа". Эти настройки гарантируют, что ваши изменения будут применяться как в режим отладки, так и при выпуске.
  3. В левой области диалогового окна "Страницы свойств проекта" выберите "Общие свойства>конфигурации".
  4. Выберите для свойства Тип конфигурации значение Статическая библиотека (.lib).
  5. Измените стандарт языка C++ на ISO C++20 Standard (/std:c++20) (или более поздней версии).
  6. В левой панели диалогового окна "Страницы свойств проекта" выберите Свойства конфигурации>C/C++>Общие.
  7. В раскрывающемся списке "Источники сканирования для зависимостей модуля" выберите "Да". (Этот параметр приводит к тому, что компилятор сканирует код для зависимостей, которые могут быть встроены в блоки заголовков): Снимок экрана: параметр свойства зависимостей модуля сканирования.
  8. Нажмите кнопку "ОК ", чтобы закрыть диалоговое окно "Страницы свойств проекта". Соберите решение, выбрав Построить>Построить решение в главном меню.

Ссылка на библиотеку заголовочных файлов

Чтобы импортировать <iostream> и <vector> как блоки заголовков из статической библиотеки, создайте проект, ссылающийся на статическую библиотеку следующим образом:

  1. Не закрывая текущее решение, в меню Visual Studio выберите Файл>Добавить>Новый проект.

  2. В мастере создания проекта выберите шаблон консольного приложения C++ и нажмите кнопку "Далее".

  3. Присвойте новому проекту пошаговое руководство. Измените выпадающее меню «Решение» на «Добавить в решение». Выберите "Создать ", чтобы создать проект и добавить его в решение.

  4. Измените содержимое исходного файла Walkthrough.cpp следующим образом:

    import <iostream>;
    import <vector>;
    
    int main()
    {
        std::vector<int> numbers = {0, 1, 2};
        std::cout << numbers[1];
    }
    

Для блока заголовка требуется /std:c++20 опция (или более поздняя). Задайте стандарт языка, выполнив следующие действия.

  1. В Обозревателе решений щелкните правой кнопкой мыши на проект Walkthrough и выберите Свойства, чтобы открыть диалоговое окно свойств проекта: Снимок экрана, показывающий настройку языкового стандарта на предварительную версию.
  2. В левой области диалогового окна свойств проекта "Walkthrough" выберите свойства конфигурации>Общие.
  3. В раскрывающемся списке "Стандартный язык C++" выберите СТАНДАРТ ISO C++20 (/std:c++20) (или более поздней версии).
  4. Нажмите кнопку "ОК ", чтобы закрыть диалоговое окно "Страницы свойств проекта".

В проекте Пошагового руководства добавьте ссылку на проект SharedPrj, выполнив следующие действия:

  1. В проекте Walkthrough выберите узел Ссылки, затем нажмите Добавить ссылку. Выберите SharedPrj в списке проектов: Снимок экрана, показывающий диалоговое окно Добавление этой ссылки приводит к тому, что система сборки будет использовать единицы заголовков, созданные SharedPrj всякий раз, когда import в проекте Walkthrough соответствует одной из созданных единиц заголовков в SharedPrj.
  2. Нажмите кнопку "ОК", чтобы закрыть диалоговое окно "Добавить ссылку".
  3. Щелкните правой кнопкой мыши проект Walkthrough и выберите пункт Назначить запускаемым проектом.
  4. Постройте решение. (Используйте Build>Build Solution в главном меню.) Запустите его, чтобы увидеть, что он создает ожидаемые результаты: 1

Преимуществом этого подхода является то, что вы можете ссылаться на проект статической библиотеки из любого проекта, чтобы использовать в нем блоки заголовков. В этом примере статическая библиотека содержит единицы заголовка <vector> и <iostream>.

Вы можете создать единый проект статической библиотеки, содержащий все часто используемые заголовки STL, импортируемые из различных ваших проектов. Либо можно создавать небольшие проекты общих библиотек, где будут находиться различные группы библиотек STL, импортируемые как блоки заголовков. Затем можно сослаться на эти проекты общего блока заголовка по мере необходимости.

В результате должна увеличиться пропускная способность сборки, поскольку импорт блока заголовка значительно сокращает работу, которую должен выполнить компилятор.

При использовании этого подхода с собственными проектами создайте проект статической библиотеки с параметрами компилятора, совместимыми с проектом, который ссылается на него. Например, проекты STL следует создавать с параметром компилятора /EHsc, чтобы включить обработку исключений, и так же следует поступать с проектами, ссылающимися на проект статической библиотеки.

Используйте /translateInclude

Параметр /translateInclude компилятора (доступный в диалоговом окне "Страницы свойств проекта" в разделе C/C++>General>Translate Includes to Imports) упрощает использование библиотеки единиц заголовка в старых проектах, которые используют библиотеки STL. Это устраняет необходимость изменения директив с #include на import в вашем проекте, но по-прежнему дает преимущество импорта единиц заголовков вместо их включения.

Например, если у вас есть #include <vector> в проекте, и вы ссылаетесь на статическую библиотеку, содержащую блок заголовка для <vector>, вам не нужно вручную изменять #include <vector> на import <vector>; в вашем исходном коде. Вместо этого компилятор автоматически обрабатывает #include <vector> как import <vector>;. Дополнительные сведения об этом подходе см. в разделе "Подход 2. Сканирование включает в себя импорт заголовков STL". Не все файлы заголовков STL можно скомпилировать в единицу заголовка. Для header-units.json, поставляемого вместе с Visual Studio, представлен список файлов заголовков STL, которые можно скомпилировать в единицы заголовков. Заголовок, который использует макросы для указания его поведения, часто не может быть скомпилирован в единицу заголовка.

Оператор #include, который не относится к единице заголовка, трактуется как обычный #include.

Использование одних блоков заголовков в разных проектах

Блоки заголовков, созданные проектом статической библиотеки, автоматически доступны для всех проектов, которые напрямую или косвенно ссылаются на него. Существуют параметры проекта, позволяющие выбрать, какие блоки заголовков должны быть автоматически доступны для всех ссылающихся проектов. Они доступны среди параметров проекта в разделе Каталоги VC++.

  1. В Обозреватель решений щелкните проект правой кнопкой мыши и выберите пункт "Свойства", чтобы открыть диалоговое окно "Страницы свойств проекта".
  2. В левой области диалогового окна выберите Свойства конфигурации>каталоги VC++: Снимок экрана, показывающий свойства общедоступного содержимого проекта, такие как общедоступные каталоги включения и все файлы заголовков являются общедоступными.

Следующие свойства управляют видимостью заголовочных единиц в системе сборки.

  • Публичные каталоги инклудов указывают каталоги проектов для заголовочных единиц, которые должны автоматически добавляться в путь подключения в ссылающихся проектах.
  • Общедоступные каталоги модулей C++ указывают, какие каталоги проектов содержат блоки заголовков, которые должны быть доступны для ссылки на проекты. Это свойство позволяет сделать некоторые блоки заголовков общедоступными. Это видно для других проектов, поэтому поместите блоки заголовков, которые вы хотите поделиться здесь. Если вы используете эту настройку, для удобства укажите Public Include Directories для автоматического добавления публичных заголовков в путь включения в проектах ссылок.
  • Все модули являются общедоступными: при использовании блоков заголовков, созданных в рамках проекта DLL, символы должны экспортироваться из библиотеки DLL. Чтобы автоматически экспортировать символы модуля, задайте для этого свойства значение "Да".

Использование предварительно созданного файла модуля

Как правило, самый простой способ повторного использования единиц заголовков среди решений — ссылаться на проект единицы общего заголовка из каждого решения.

Если необходимо использовать встроенный блок заголовка, для которого у вас нет проекта, можно указать, где находится созданный .ifc файл, чтобы импортировать его в решение. Для доступа к этому параметру:

  1. В главном меню выберите "Свойства проекта>", чтобы открыть диалоговое окно "Страницы свойств проекта".
  2. В левой области диалогового окна выберите свойства>конфигурации C/C++>General.
  3. В Дополнительных Зависимостях Модуля добавьте модули для ссылок, разделённые точкой с запятой. Ниже приведен пример формата, используемого для дополнительных зависимостей модулей: ModuleName1=Path\To\ModuleName1.ifc; ModuleName2=Path\To\ModuleName2.ifcСнимок экрана: свойства страниц свойств проекта в разделе

Выбор из нескольких копий заголовочного блока

Если вы ссылаетесь на проекты, создающие несколько единиц заголовков, либо с одинаковым именем, либо для одного файла заголовка, необходимо указать, какой из них следует использовать. Возможно, у вас есть разные версии блока заголовков, созданные с разными параметрами компилятора, например, и необходимо указать версию, соответствующую параметрам проекта.

Используйте свойство Дополнительные зависимости единицы заголовка проекта, чтобы устранить столкновения, указав единицу заголовка, которую следует использовать. В противном случае невозможно предсказать, какой из них выбран.

Чтобы задать свойство дополнительных зависимостей заголовочных единиц, выполните следующие действия.

  1. В главном меню выберите "Свойства проекта>", чтобы открыть диалоговое окно "Страницы свойств проекта".
  2. В левой области диалогового окна выберите свойства>конфигурации C/C++>General.
  3. Укажите, какие модули или файлы единиц заголовка следует использовать в дополнительных зависимостях блока заголовков для разрешения конфликтов. Используйте этот формат для дополнительных зависимостей единиц заголовка: Path\To\Header1.h= Path\To\HeaderUnit1.ifc;Path\To\Header2.h= Path\To\ HeaderUnit2.ifcСнимок экрана, показывающий настройки дополнительных зависимостей единиц заголовка в диалоговом окне

Внимание

Убедитесь, что проекты с общими блоками заголовков созданы с совместимыми параметрами компиляции. Если реализовать блок заголовка с параметрами компиляции, отличными от указанных при его создании, компилятор выдаст предупреждения.

Примечание.

Чтобы использовать блоки заголовков, скомпилированные как часть проекта DLL, задайте параметру Все модули являются общедоступными значение Да.

Подход 2. Сканирование включает в себя импорт заголовков STL

Другой способ импорта библиотек STL заключается в том, чтобы Visual Studio сканировала заголовки STL, #include которые вы используете в своем проекте, и компилировала их в единицы заголовков. Затем компилятор импортирует, а не включает эти заголовки.

Этот параметр удобен, если проект включает множество заголовочных файлов STL, находящихся в нескольких файлах, или если скорость сборки не важна. Этот параметр не гарантирует, что блок заголовка для определенного файла заголовка создается только один раз. Однако это полезно, если у вас есть большая база кода: вам не нужно изменять исходный код, чтобы воспользоваться преимуществами единиц заголовков для многих используемых библиотек STL.

Этот подход менее гибкий, чем подход статической библиотеки, так как он не дает возможности повторно использовать встроенные блоки заголовков в других проектах. Этот подход может быть неуместным для более крупных проектов: он не гарантирует оптимальное время сборки, поскольку все источники должны быть просканированы для #include инструкций.

Не все файлы заголовков можно автоматически преобразовать в единицы заголовков. Например, заголовки, зависящие от условной компиляции с помощью макросов, не должны быть преобразованы в единицы заголовков. Существует список разрешенных в виде файла header-units.json для заголовков STL, которые компилятор использует при указании /translateInclude. Он определяет, какие заголовки STL можно скомпилировать в единицы заголовков. Файл header-units.json находится в каталоге установки для Visual Studio. Например, %ProgramFiles%\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.30.30705\include\header-units.json. Если файл заголовка STL не находится в списке, он рассматривается как обычный #include вместо импорта его в виде единицы заголовка. Еще одним преимуществом header-units.json файла является предотвращение дублирования символов в встроенных блоках заголовков. То есть, если компиляция единицы заголовка несколько раз приводит к другому заголовку библиотеки, символы не будут повторяться.

Чтобы попробовать этот подход, создайте проект, включающий две библиотеки STL. Затем измените свойства проекта таким образом, чтобы он импортирует библиотеки в виде единиц заголовков вместо их включения, как описано в следующем разделе.

Создание проекта консольного приложения С++

Выполните следующие действия, чтобы создать проект, включающий две библиотеки STL: <iostream> и <vector>.

  1. В Visual Studio создайте проект консольного приложения C++.

  2. Замените содержимое исходного файла следующим образом:

    #include <iostream>
    #include <vector>
    
    int main()
    {
        std::vector<int> numbers = {0, 1, 2};
        std::cout << numbers[1];
    }
    

Задание параметров проекта и запуск проекта

Следующие шаги задают опцию, которая приводит к сканированию включенных заголовков компилятором для преобразования в единицы заголовков. Они также задают параметр, который заставляет компилятор обрабатывать #include так, как если бы вы написали import, для файлов заголовков, которые можно рассматривать как модули заголовков.

  1. В главном меню выберите "Свойства проекта>", чтобы открыть диалоговое окно "Страницы свойств проекта".
  2. Выберите все конфигурации в раскрывающемся списке "Конфигурация" и выберите "Все платформы" в раскрывающемся списке "Платформа". Эти параметры гарантируют, что изменения применяются как при сборке для отладки, так и для релизов и других конфигураций.
  3. В левой области диалогового окна выберите свойства>конфигурации C/C++>General.
  4. Установите для параметра Проверка источников на наличие зависимостей модуля значение Да. Этот параметр гарантирует, что все совместимые файлы заголовков компилируются в единицы заголовков.
  5. Задайте для преобразования импортазначение "Да". Этот параметр компилирует файлы заголовков STL, перечисленные в файле header-unit.json, в виде единиц заголовков, а затем импортирует их вместо их обработки препроцессором в #include. Снимок экрана: параметр свойства зависимостей модуля сканирования в страницах свойств проекта.
  6. Нажмите кнопку "ОК ", чтобы сохранить изменения и закрыть диалоговое окно "Страницы свойств проекта".

Для использования заголовочных единиц требуется параметр /std:c++20, либо более поздняя версия. Чтобы изменить стандарт языка C++, используемый компилятором, выполните следующие действия.

  1. В главном меню выберите "Свойства проекта>", чтобы открыть диалоговое окно "Страницы свойств проекта".
  2. Выберите все конфигурации в раскрывающемся списке "Конфигурация" и выберите "Все платформы" в раскрывающемся списке "Платформа". Эти параметры обеспечивают, что ваши изменения применяются не только для отладки и выпуска, но и для других конфигураций.
  3. В левой области диалогового окна "Страницы свойств проекта" выберите "Общие свойства>конфигурации".
  4. В раскрывающемся списке "Стандартный язык C++" выберите СТАНДАРТ ISO C++20 (/std:c++20) (или более поздней версии).
  5. Нажмите кнопку "ОК ", чтобы сохранить изменения и закрыть диалоговое окно "Страницы свойств проекта".
  6. В главном меню соберите решение, выбрав Сборка>Собрать решение.

Запустите решение, чтобы убедиться, что он создает ожидаемые выходные данные: 1

Основной критерий выбора этого подхода заключается в учете баланса между удобством и стоимостью сканирования всех файлов, чтобы определить, какие файлы заголовков следует создавать в виде единиц заголовков.

См. также

Сравнение единиц заголовков, модулей и предварительно скомпилированных заголовков
Руководство. Импорт стандартной библиотеки C++ с помощью модулей
Пошаговое руководство. Создание и импорт единиц заголовков в проектах Microsoft C++
/translateInclude