close
Пространства имён
Варианты
Действия

Фазы трансляции

Материал из cppreference.com
 
 
Язык С++
Общие темы
Управление потоком
Операторы условного выполнения
if
Операторы итерации (циклы)
Операторы перехода
Функции
Объявление функции
Выражение лямбда-функции
Спецификатор inline
Спецификации динамических исключений (до C++17*)
Спецификатор noexcept (C++11)
Исключения
Пространства имён
Типы
Спецификаторы
decltype (C++11)
auto (C++11)
alignas (C++11)
Спецификаторы длительности хранения
Инициализация
Выражения
Альтернативные представления
Литералы
Логические - Целочисленные - С плавающей запятой
Символьные - Строковые - nullptr (C++11)
Определяемые пользователем (C++11)
Утилиты
Атрибуты (C++11)
Types
Объявление typedef
Объявление псевдонима типа (C++11)
Casts
Неявные преобразования - Явные преобразования
static_cast - dynamic_cast
const_cast - reinterpret_cast
Выделение памяти
Классы
Свойства функции класса
explicit (C++11)
static
Специальные функции-элементы
Шаблоны
Разное
 
 

Исходный файл C++ должен быть препроцессирован компилятором так, как будто последовательно выполняются следующие фазы:

Фаза 1

1) Отдельные байты файла исходного кода сопоставляются (определяемым реализацией образом) с символами основного исходного набора символов. В частности, зависящие от ОС индикаторы конца строки заменяются символами новой строки.
2) Набор допустимых символов исходного файла определяется реализацией (начиная с C++11). Любой символ исходного файла, который не может быть сопоставлен с символом из базового исходного набора символов заменяется его универсальным именем символа (экранированным с помощью \u или \U) или какой-либо определённой реализацией формой, которая обрабатывается эквивалентно.
3) Последовательности триграфов заменяются соответствующими односимвольными представлениями.
(до C++17)
(до C++23)

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

  • Если входной файл определён как файл UTF-8, то он должен быть правильно сформированной последовательностью кодовых единиц UTF-8, и он декодируется для создания последовательности скалярных значений Юникода. Затем формируется последовательность элементов набора символов трансляции путём сопоставления каждого скалярного значения Юникода с соответствующим элементом набора символов трансляции. В результирующей последовательности каждая пара символов во входной последовательности, состоящая из возврата каретки (U+000D), за которым следует перевод строки (U+000A), а также каждый возврат каретки (U+000D), за которым сразу не следует перевод строки (U+000A), заменяется одним символом новой строки.
  • Для любого другого типа входного файла, поддерживаемого реализацией, символы сопоставляются (определяемым реализацией способом) с последовательностью элементов набора символов трансляции. В частности, зависящие от ОС индикаторы конца строки заменяются символами новой строки.
(начиная с C++23)

Фаза 2

1) Всякий раз, когда символ \ (обратная косая черта, backslash) появляется в конце строки (сразу же за которым следует ноль или более пробелов, кроме символа новой строки, за которыми следует (начиная с C++23) символ новой строки), оба символа удаляются, объединяя две физические строки исходного кода в одну логическую строку исходного кода. Это однопроходная операция. Строка, заканчивающаяся двумя символами \, за которой следует пустая строка, не объединяет три строки в одну.
2) Если после этого шага непустой файл исходного кода не закончился символом новой строки (независимо от того, не было ли у него символа новой строки изначально или он закончился новой строкой, непосредственно перед которой стояла обратная косая черта), добавляется завершающий символ новой строки.

Фаза 3

1) Файл исходного кода разбивается на комментарии, последовательности пробельных символов и лексемы препроцессора, которые являются следующим:
a) имена заголовочных файлов, такие как <iostream> или "myfile.h"
b) токены-заполнители, созданные путём предварительной обработки директив import и module (т.е. import XXX; и module XXX;)
(начиная с C++20)
d) числа предварительной обработки
e) символьные литералы, включая определяемые пользователем символьные литералы (начиная с C++11)
f) строковые литералы, включая определяемые пользователем строковые литералы (начиная с C++11)
g) операторы и знаки пунктуации (включая альтернативные токены), такие как +, <<=, <%, ## или and
h) отдельные непробельные символы, которые не попадают ни в одну другую категорию
Программа некорректна, если символ, соответствующий этой категории,
2) Любые преобразования, выполненные во время фазы 1 и (до C++23) фазы 2 между начальной и конечной двойной кавычкой любого сырого строкового литерала отменяются.
(начиная с C++11)
3) Каждый комментарий заменяется одним пробелом.

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

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

a) символьный литерал (символьная-последовательность-c)
b) строковый литерал (символьная-последовательность-s и символьная-последовательность-r), исключая разделители (символьная-последовательность-d)
c) имя файла для включения (символьная-последовательность-h и символьная-последовательность-q)
(начиная с 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

1 ) Выполняется препроцессор.
2 ) Каждый файл, введённый с помощью директивы #include, рекурсивно проходит фазы 1-4.
3 ) В конце этой фазы все директивы препроцессора удаляются из исходного кода.

Фаза 5

1) Все символы в символьных литералах и строковых литералах преобразуются из исходного набора символов в encoding (которая может быть многобайтовой кодировкой символов, такой как UTF-8, при условии, что 96 символов базового набора символов имеют однобайтовые представления).
2) Управляющие последовательности и универсальные имена символов в символьных литералах и строковых литералах, за исключением необработанных литералов, расширяются и преобразуются в символьную кодировку. Если символ, указанный универсальным именем символа, не может быть закодирован как одиночная кодовая точка в соответствующей символьной кодировке, результат определяется реализацией, но гарантированно не будет нулевым (широким) символом.

Примечание: преобразованием, выполняемым на этом этапе, в некоторых реализациях можно управлять с помощью параметров командной строки: 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 по Фазы трансляции