Фазы трансляции
Исходный файл C++ должен быть препроцессирован компилятором так, как будто последовательно выполняются следующие фазы:
Фаза 1
|
1) Отдельные байты файла исходного кода сопоставляются (определяемым реализацией образом) с символами основного исходного набора символов. В частности, зависящие от ОС индикаторы конца строки заменяются символами новой строки.
2) Набор допустимых символов исходного файла определяется реализацией (начиная с C++11). Любой символ исходного файла, который не может быть сопоставлен с символом из базового исходного набора символов заменяется его универсальным именем символа (экранированным с помощью
\u или \U) или какой-либо определённой реализацией формой, которая обрабатывается эквивалентно.
|
(до C++23) | ||
|
Входные файлы, представляющие собой последовательность кодовых единиц UTF-8 (файлы UTF-8), гарантированно поддерживаются. Набор других поддерживаемых типов входных файлов определяется реализацией. Если набор не пуст, тип входного файла определяется способом, определяемым реализацией, который включает средства определения входных файлов как файлов UTF-8, независимо от их содержимого (распознавание метки порядка байтов недостаточно).
|
(начиная с C++23) |
Фаза 2
\ (обратная косая черта, backslash) появляется в конце строки (сразу же за которым следует ноль или более пробелов, кроме символа новой строки, за которыми следует (начиная с C++23) символ новой строки), оба символа удаляются, объединяя две физические строки исходного кода в одну логическую строку исходного кода. Это однопроходная операция. Строка, заканчивающаяся двумя символами \, за которой следует пустая строка, не объединяет три строки в одну.Фаза 3
<iostream> или "myfile.h"|
b) токены-заполнители, созданные путём предварительной обработки директив
import и module (т.е. import XXX; и module XXX;) |
(начиная с C++20) |
- апостроф (
', U+0027), - кавычка (
", U+0022), или - символ, не входящий в базовый набор символов.
|
2) Любые преобразования, выполненные во время фазы 1 и (до C++23) фазы 2 между начальной и конечной двойной кавычкой любого сырого строкового литерала отменяются.
|
(начиная с C++11) |
Новые строки сохраняются, и не указано, могут ли последовательности пробельных символов, не являющихся символами новой строки, быть свёрнуты в одиночные пробелы.
|
Поскольку символы из исходного файла используются для формирования следующего токена предварительной обработки (т.е. не используются как часть комментария или других форм пробелов), универсальные имена символов распознаются и заменяются указанным элементом набора символов трансляции, за исключением случаев соответствия последовательности символов в: b) строковый литерал (символьная-последовательность-s и символьная-последовательность-r), исключая разделители (символьная-последовательность-d)
|
(начиная с C++23) |
Если последовательность входных символов синтаксическим разбором была преобразована в лексемы препроцессора вплоть до данного символа, следующей лексемой препроцессора обычно будет последовательность символов максимальной длины, из которой можно сформировать лексему, даже если последующий анализ закончится неудачей. Данный подход известен как правило максимальной длины (maximal munch).
int foo = 1;
int bar = 0xE+foo; // ошибка, недействительное число препроцессора 0xE+foo
int baz = 0xE + foo; // OK
int quux = bar+++++baz; // ошибка: распознается как bar++ ++ +baz, а не как bar++ + ++baz.
Исключения из правила максимального куска:
#define R "x"
const char* s = R"y"; // некорректный необработанный строковый литерал,
// не "x" "y"
const char* s2 = R"(a)" "b)"; // необработанный строковый литерал,
// за которым идёт обычный строковый литерал
struct Foo { static const int v = 1; };
std::vector<::Foo> x; // OK, <: не рассматривается как альтернативная лексема для [
extern int y<::>; // OK, эксивалентно extern int y[].
int z<:::Foo::value:>; // OK, int z[::Foo::value];
|
(начиная с C++11) |
- Токены предварительной обработки имени заголовка формируются только в рамках директивы #include или import (начиная с C++20) или в выражении __has_include (начиная с C++17).
std::vector<int> x; // OK, <int> не имя заголовочного файла
Фаза 4
Фаза 5
Примечание: преобразованием, выполняемым на этом этапе, в некоторых реализациях можно управлять с помощью параметров командной строки: gcc и clang используют -finput-charset для указания кодировки исходного набора символов, -fexec-charset и -fwide-exec-charset, чтобы указать одинарную и расширенную символьные кодировки соответственно, в то время как Visual Studio 2015 Обновление 2 и более поздние версии используют /source-charset и /execution-charset, чтобы указать исходный набор символов и символьную кодировку соответственно.
Фаза 6
Смежные строковые литералы объединяются.
Фаза 7
Выполняется компиляция: каждая лексема препроцессора преобразуется в лексему. Эти лексемы анализируются синтаксически и семантически, а затем преобразуются как единицы трансляции.
Фаза 8
Каждая единица трансляции проверяется, чтобы создать список необходимых экземпляров шаблонов, включая те, что требуют явного создания. Находятся определения шаблонов и требуемые инстанцирования выполняются для создания экземпляров единиц.
Фаза 9
Единицы трансляции, единицы инстанцирования и компоненты библиотек, необходимые для удовлетворения внешних ссылок, собираются в образ программы, который содержит информацию, необходимую для выполнения в ее среде выполнения.
Примечания
Некоторые компиляторы не реализуют единицы инстанцирования (также известных как репозитории шаблонов или шаблонные регистры) и просто компилируют каждое инстанцирование шаблона в фазе 7, сохраняя код в объектный файл, откуда он явно или неявно запрашивается, а затем компоновщик на этапе 9 сворачивает эти скомпилированные инстанцирования в одно.
Отчёты о дефектах
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:
| Номер | Применён | Поведение в стандарте | Корректное поведение |
|---|---|---|---|
| CWG 787 | C++98 | поведение было неопределённым, если непустой исходный файл не заканчивался символом новой строки в конце фазы 2 |
в этом случае добавляется завершающий символ новой строки |
| CWG 1775 | C++11 | формирование универсального имени символа внутри необработанного строкового литерала на этапе 2 приводило к неопределённому поведению |
чётко определено |
| WG не указан | C++98 | универсальные имена символов не могли быть сформированы путём сращивания строк или конкатенации токенов |
позволено |
Ссылки
- C++23 стандарт (ISO/IEC 14882:2023):
- 5.2 Фазы трансляции [lex.phases]
- C++20 стандарт (ISO/IEC 14882:2020):
- 5.2 Фазы трансляции [lex.phases]
- C++17 стандарт (ISO/IEC 14882:2017):
- 5.2 Фазы трансляции [lex.phases]
- C++14 стандарт (ISO/IEC 14882:2014):
- 2.2 Фазы трансляции [lex.phases]
- C++11 стандарт (ISO/IEC 14882:2011):
- 2.2 Фазы трансляции [lex.phases]
- C++03 стандарт (ISO/IEC 14882:2003):
- 2.1 Фазы трансляции [lex.phases]
- C++98 стандарт (ISO/IEC 14882:1998):
- 2.1 Фазы трансляции [lex.phases]
Смотрите также
Документация C по Фазы трансляции
|