Включение исходного файла — cppreference.com
Включает другой исходный файл в текущий исходный файл в строке сразу после директивы.
Синтаксис
#include < последовательность-h-символов > новая-строка
|
(1) | ||||||||
#include " последовательность-q-символов " новая-строка
|
(2) | ||||||||
#include pp-токены новая-строка
|
(3) | ||||||||
__has_include ( " последовательность-q-символов " )__has_include ( < последовательность-h-символов > )
|
(4) | (начиная с C++17) | |||||||
__has_include ( строковый-литерал )__has_include ( < h-pp-токены > )
|
(5) | (начиная с C++17) | |||||||
Любые маркеры предварительной обработки (макроконстанты или выражения) разрешены в качестве аргументов для #include и __has_include (начиная с C++17), пока они расширяются до последовательности символов, окружённой < > или " ".
1) Выполняет поиск заголовка, однозначно идентифицируемого последовательностью-h-символов, и заменяет директиву всем содержимым заголовка.
2) Ищет исходный файл, идентифицированный последовательностью-q-символов, и заменяет директиву всем содержимым исходного файла. Оно может вернуться к (1) и рассматривать последовательность-q-символов как идентификатор заголовка.
3) Если ни (1), ни (2) не совпадают, pp-токены подвергнется замене макроса. После замены снова будет предпринята попытка сопоставления директивы с (1) или (2).
4) Проверяет, доступен ли для включения заголовочный или исходный файл.
5) Если (4) не совпадает, h-pp-токены подвергнется замене макроса. После замены снова будет предпринята попытка сопоставления директивы с (4).
| новая-строка | — | Символ новой строки |
| последовательность-h-символов | — | Последовательность из одного или нескольких символов h-символ, где появление любого из следующего поддерживается условно с семантикой, определяемой реализацией:
|
| h-символ | — | Любой элемент исходного набора символов (до C++23)набора символов трансляции (начиная с C++23) кроме символа новой строки и >
|
| последовательность-q-символов | — | Последовательность из одного или нескольких q-символ, где появление любого из следующих элементов поддерживается условно с семантикой, определяемой реализацией:
|
| q-символ | — | Любой элемент исходного набора символов (до C++23)набор символов трансляции (начиная с C++23) кроме символа новой строки и "
|
| pp-токены | — | Последовательность из одного или нескольких токенов предварительной обработки |
| строковый-литерал | — | Строковый литерал |
| h-pp-токены | — | Последовательность из одного или нескольких токенов предварительной обработки кроме >
|
Объяснение
1) Ищет в последовательности определённых реализацией мест заголовок, однозначно идентифицируемый последовательностью-h-символов, и вызывает замену этой директивы всем содержимым заголовка. То, как указываются места или идентифицируется заголовок, определяется реализацией.
2) Вызывает замену этой директивы всем содержимым исходного файла, указанного последовательностью-q-символов. Именованный исходный файл ищется определяемым реализацией способом. Если этот поиск не поддерживается или поиск не удался, директива обрабатывается повторно, как если бы она считывала синтаксис (1) с идентичной содержащейся последовательностью (включая символы >, если они есть) из исходной директивы.
3) Токены предварительной обработки после include в директиве обрабатываются так же, как и в обычном тексте (т.е. каждый идентификатор, определённый в настоящее время как имя макроса, заменяется своим замещающим списком токенов предварительной обработки). Если полученная после всех замен директива не соответствует ни одной из двух предыдущих форм, поведение не определено. Метод, с помощью которого последовательность токенов предварительной обработки между парой токенов предварительной обработки < и > или парой символов " объединяется в
токен предварительной обработки имени одного заголовка определяется реализацией.
4) Заголовок или исходный файл, идентифицированный последовательностью-h-символов или последовательностью-q-символов, ищется, как если бы эта последовательность токенов предварительной обработки была pp-токены в синтаксисе (3), за исключением того, что дальнейшее расширение макроса не выполняется. Если бы такая директива не соответствовала
синтаксическим требованиям директивы #include, программа была бы некорректна. Выражение __has_include оценивается как 1, если поиск исходного файла завершается успешно, и как 0, если поиск не удался.
5) Эта форма рассматривается только в том случае, если синтаксис (4) не соответствует, и в этом случае токены предварительной обработки обрабатываются так же, как в обычном тексте.
|
Если заголовок, определяемый именем-заголовка (т.е.
|
(начиная с C++20) |
__has_include можно раскрыть в выражении
#if и
#elif. Он рассматривается как определённый макрос в
#ifdef,
#ifndef,
#elifdef,
#elifndef (начиная с C++23) и defined, но не может быть использован где нибудь ещё.
Примечание
Типичные реализации выполняют поиск только в стандартных каталогах включения для синтаксиса (1). Стандартная библиотека C++ и стандартная библиотека C неявно включены в эти стандартные включаемые каталоги. Стандартные включаемые каталоги обычно могут контролироваться пользователем с помощью параметров компилятора.
Целью синтаксиса (2) является поиск файлов, которые не контролируются реализацией. Типичные реализации сначала ищут в каталоге, в котором находится текущий файл, а затем возвращаются к (1).
Когда файл включен, он обрабатывается фазами трансляции 1-4, которые могут рекурсивно включать раскрытие вложенных директив #include. Чтобы избежать повторного включения одного и того же файла и бесконечной рекурсии, когда файл включает себя, возможно, транзитивно, обычно используются меры защиты заголовков: весь заголовок оборачивается в
#ifndef FOO_H_INCLUDED /* любое имя, однозначно сопоставленное с именем файла */ #define FOO_H_INCLUDED // содержимое файла здесь #endif
Многие компиляторы также реализуют нестандартную pragma #pragma once с аналогичными эффектами: она отключает обработку файла, если этот файл (где идентификатор файла определяется в зависимости от ОС) уже включен.
Последовательность символов, похожая на управляющую последовательность в последовательности-q-символов или последовательности-h-символов, может привести к ошибке, интерпретируясь как символ, соответствующий управляющей последовательности, или иметь совершенно другое значение, в зависимости от реализации.
Результат __has_include для 1 означает только то, что существует заголовочный или исходный файл с указанным именем. Это не означает, что заголовочный или исходный файл, если он включен, не вызовет ошибки или будет содержать что-нибудь полезное. Например, в реализации C++, которая поддерживает режимы C++14 и C++17 (и предоставляет __has_include в своём режиме C++14 в качестве соответствующего расширения), __has_include(<optional>) может быть 1 в режиме C++14, но на самом деле #include <optional> может вызвать ошибку.
Пример
#if __has_include(<optional>) # include <optional> # define has_optional 1 template<class T> using optional_t = std::optional<T>; #elif __has_include(<experimental/optional>) # include <experimental/optional> # define has_optional -1 template<class T> using optional_t = std::experimental::optional<T>; #else # define has_optional 0 # include <utility> template<class V> class optional_t { V v_{}; bool has_{false}; public: optional_t() = default; optional_t(V&& v) : v_(v), has_{true} {} V value_or(V&& alt) const& { return has_ ? v_ : alt; } /*...*/ }; #endif #include <iostream> int main() { if (has_optional > 0) std::cout << "<optional> присутствует\n"; else if (has_optional < 0) std::cout << "<experimental/optional> присутствует\n"; else std::cout << "<optional> не присутствует\n"; optional_t<int> op; std::cout << "op = " << op.value_or(-1) << '\n'; op = 42; std::cout << "op = " << op.value_or(-1) << '\n'; }
Вывод:
<optional> присутствует op = -1 op = 42