Namespaces
Variants

Phases of translation

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Исходные файлы C++ обрабатываются компилятором для создания программ на C++.

Содержание

Процесс перевода

Текст программы на C++ хранится в единицах, называемых исходными файлами .

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

  1. Сопоставляет каждый исходный файл с последовательностью символов.
  2. Преобразует каждую последовательность символов в последовательность токенов препроцессора, разделенных пробелами.
  3. Преобразует каждый токен препроцессора в токен, формируя последовательность токенов.
  4. Преобразует каждую последовательность токенов в единицу трансляции.

Программа на C++ может быть сформирована из транслированных единиц трансляции. Транслированные единицы трансляции и инстанцированные единицы (инстанцированные единицы описаны в фазе 8 ниже) могут сохраняться индивидуально или сохраняться в библиотеку. Несколько единиц трансляции взаимодействуют друг с другом через (например) символы с внешней линковкой или файлы данных. Единицы трансляции могут транслироваться отдельно, а затем линковаться для создания исполняемой программы.

Вышеуказанный процесс может быть организован в 9 фаз трансляции .

Токены препроцессора

Препроцессинговый токен — это минимальный лексический элемент языка на этапах трансляции с 3 по 6.

Категории токенов препроцессора:

(since C++20)
Программа является некорректной, если символ, соответствующий этой категории,

Препроцессорные числа

Множество токенов препроцессора для препроцессингового числа является надмножеством объединения множеств токенов целочисленных литералов и литералов с плавающей точкой :

. (необязательно) digit pp-continue-seq  (необязательно)
digit - одна из цифр 0-9
pp-continue-seq - последовательность pp-continue ов

Каждый pp-continue представляет собой одно из следующего:

identifier-continue (1)
exp-char sign-char (2)
. (3)
digit (4) (начиная с C++14)
nondigit (5) (начиная с C++14)
identifier-continue - любой непервоначальный символ допустимого идентификатора
exp-char - один из P , p , (since C++11) E и e
sign-char - один из + и -
digit - одна из цифр 0-9
nondigit - один из латинских букв A/a-Z/z и подчеркивания

Препроцессорное число не имеет типа или значения; оно приобретает и то, и другое после успешного преобразования в токен целочисленного/вещественного литерала.

Пробельные символы

Пробельные символы состоят из комментариев , символов пробела или обоих элементов.

Следующие символы являются пробельными символами:

  • символ табуляции (U+0009)
  • символ перевода строки / символ новой строки (U+000A)
  • символ вертикальной табуляции (U+000B)
  • символ прогона страницы (U+000C)
  • пробел (U+0020)

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

  • Это не разделитель в имени заголовка, символьном литерале и строковом литерале.
  • Предпроцессорные токены, разделенные пробелами, содержащими символы новой строки, не могут формировать директивы препроцессора .
#include "my header"        // OK, использование имени заголовка, содержащего пробелы
#include/*hello*/<iostream> // OK, использование комментария в качестве пробела
#include
<iostream> // Ошибка: директива #include не может занимать несколько строк
"str ing"  // OK, единый токен препроцессора (строковый литерал)
' '        // OK, единый токен препроцессора (символьный литерал)

Максимальное поглощение

Максимальное поглощение — это правило, используемое на этапе 3 при разложении исходного файла на препроцессинговые токены.

Если входные данные были разобраны на препроцессорные токены до определённого символа (в противном случае следующий препроцессорный токен не будет разобран, что делает порядок разбора уникальным), следующий препроцессорный токен обычно принимается как самая длинная последовательность символов, которая может составлять препроцессорный токен, даже если это приведёт к последующему сбою анализа. Это широко известно как maximal munch .

int foo = 1;
int bar = 0xE+foo;   // Ошибка: недопустимое число препроцессора 0xE+foo
int baz = 0xE + foo; // OK

Другими словами, правило максимального поглощения действует в пользу multi-character operators and punctuators :

int foo = 1;
int bar = 2;
int num1 = foo+++++bar; // Ошибка: обрабатывается как "foo++ ++ +baz", а не "foo++ + ++baz"
int num2 = -----foo;    // Ошибка: обрабатывается как "-- -- -foo", а не "- -- --foo"

Правило максимального поглощения имеет следующие исключения:

  • Токены предварительной обработки имени заголовка формируются только в следующих случаях:
  • после include токена препроцессора в #include директиве
(начиная с C++17)
  • после токена препроцессора import в директиве import
(начиная с C++20)
std::vector<int> x; // OK, «int» не является именем заголовка
  • Если следующие три символа представляют собой < :: и последующий символ не является ни : , ни > , тогда < обрабатывается как самостоятельный токен препроцессора, а не как первый символ альтернативного токена < : .
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];"
  • Если следующие два символа являются >> и один из символов > может завершить идентификатор шаблона , этот символ обрабатывается как отдельный токен препроцессора вместо того, чтобы быть частью токена препроцессора >> .
template<int i> class X { /* ... */ };
template<class T> class Y { /* ... */ };
Y<X<1>> x3;      // OK, объявляет переменную "x3" типа "Y<X<1> >"
Y<X<6>>1>> x4;   // Синтаксическая ошибка
Y<X<(6>>1)>> x5; // OK
  • Если следующий символ начинает последовательность символов, которая может быть префиксом и начальной двойной кавычкой необработанного строкового литерала , следующим токеном препроцессора является необработанный строковый литерал. Литерал состоит из кратчайшей последовательности символов, соответствующей шаблону необработанной строки.
#define R "x"
const char* s = R"y";         // некорректный необработанный строковый литерал, не "x" "y"
const char* s2 = R"(a)" "b)"; // необработанный строковый литерал, за которым следует обычный строковый литерал
(начиная с C++11)

Токены

Токен — это минимальный лексический элемент языка на седьмой фазе трансляции.

Категории токенов:

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

Перевод выполняется as if в порядке от фазы 1 до фазы 9. Реализации ведут себя так, как если бы эти отдельные фазы происходили, хотя на практике различные фазы могут быть объединены вместе.

Фаза 1: Отображение исходных символов

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

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

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

Фаза 2: Соединение линий

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

Фаза 3: Лексический анализ

1) Исходный файл разбивается на preprocessing tokens и whitespace :
// The following #include directive can de decomposed into 5 preprocessing tokens:
//     punctuators (#, < and >)
//          │
// ┌────────┼────────┐
// │        │        │
   #include <iostream>
//     │        │
//     │        └── header name (iostream)
//     │
//     └─────────── identifier (include)
Если исходный файл заканчивается частичной лексемой препроцессора или частичным комментарием, программа является некорректной:
// Error: partial string literal
"abc
// Error: partial comment
/* comment
По мере того как символы из исходного файла потребляются для формирования следующей лексемы препроцессора (т.е. не потребляются как часть комментария или других форм пробельных символов), универсальные символьные имена распознаются и заменяются соответствующим элементом набора символов трансляции , за исключением случаев сопоставления с последовательностью символов в одной из следующих лексем препроцессора:
  • символьный литерал ( c-char-sequence )
  • строковый литерал ( s-char-sequence и r-char-sequence ), за исключением разделителей ( d-char-sequence )
  • имя заголовка ( h-char-sequence и q-char-sequence )
(начиная с C++23)


2) Любые преобразования, выполненные во время фазы 1 и (до C++23) фазы 2 между начальной и конечной двойной кавычкой любого raw string literal отменяются.
(начиная с C++11)
3) Пробельные символы преобразуются:
  • Каждый комментарий заменяется одним пробельным символом.
  • Символы новой строки сохраняются.
  • Сохраняется ли каждая непустая последовательность пробельных символов, кроме символов новой строки, или заменяется одним пробельным символом - не определено.

Фаза 4: Препроцессинг

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

Фаза 5: Определение стандартных кодировок строковых литералов

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

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

(до C++23)

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

(начиная с C++23)

Фаза 6: Конкатенация строковых литералов

Смежные string literals объединяются.

Фаза 7: Компиляция

Компиляция происходит: каждый препроцессорный токен преобразуется в токен . Токены синтаксически и семантически анализируются и переводятся как трансляционная единица .

Фаза 8: Инстанцирование шаблонов

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

Фаза 9: Linking

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

Примечания

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

Преобразование, выполняемое на фазе 5, может управляться параметрами командной строки в некоторых реализациях: gcc и clang используют - finput - charset для указания кодировки исходного набора символов, - fexec - charset и - fwide - exec - charset для указания кодировок обычных и широких литералов соответственно, тогда как Visual Studio 2015 Update 2 и более поздние версии используют / source - charset и / execution - charset для указания исходного набора символов и кодировки литералов соответственно.

(до C++23)

Некоторые компиляторы не реализуют единицы инстанцирования (также известные как template repositories или template registries ) и просто компилируют каждую инстанциацию шаблона на фазе 7, сохраняя код в объектном файле, где она неявно или явно запрошена, а затем компоновщик объединяет эти скомпилированные инстанциации в одну на фазе 9.

Отчеты о дефектах

Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены задним числом к ранее опубликованным стандартам C++.

DR Применяется к Поведение в опубликованной версии Корректное поведение
CWG 787 C++98 поведение было неопределенным, если непустой исходный файл не
заканчивается символом новой строки в конце фазы 2
добавлять завершающий символ новой строки
в этом случае
CWG 1104 C++98 альтернативный токен < : приводил к тому, что std:: vector < :: std:: string >
обрабатывался как std:: vector [ : std:: string >
добавлено дополнительное правило
лексического анализа для этого случая
CWG 1775 C++11 формирование универсального имени символа внутри сырой
строковой константы в фазе 2 приводило к неопределенному поведению
сделано определенным
CWG 2747 C++98 фаза 2 проверяла сращивание конца файла после сращивания, это было излишним проверка удалена
P2621R3 C++98 универсальные имена символов не разрешалось
формировать с помощью сращивания строк или конкатенации токенов
разрешено

Ссылки

  • Стандарт C++23 (ISO/IEC 14882:2024):
  • 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 documentation для Phases of translation