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

Les fichiers source C++ sont traités par le compilateur pour produire des programmes C++.

Table des matières

Processus de traduction

Le texte d'un programme C++ est conservé dans des unités appelées fichiers source .

Les fichiers source C++ subissent une traduction pour devenir une unité de traduction , comprenant les étapes suivantes :

  1. Associe chaque fichier source à une séquence de caractères.
  2. Convertit chaque séquence de caractères en une séquence de jetons de prétraitement, séparés par des espaces.
  3. Convertit chaque jeton de prétraitement en jeton, formant une séquence de jetons.
  4. Convertit chaque séquence de jetons en unité de traduction.

Un programme C++ peut être formé à partir d'unités de traduction traduites. Les unités de traduction traduites et les unités instanciées (les unités instanciées sont décrites dans la phase 8 ci-dessous) peuvent être sauvegardées individuellement ou sauvegardées dans une bibliothèque. Plusieurs unités de traduction communiquent entre elles par (par exemple) des symboles avec liaison externe ou des fichiers de données. Les unités de traduction peuvent être traduites séparément puis liées ultérieurement pour produire un programme exécutable.

Le processus ci-dessus peut être organisé en 9 phases de traduction .

Jetons de prétraitement

Un jeton de prétraitement est l'élément lexical minimal du langage dans les phases de traduction 3 à 6.

Les catégories de jetons de préprocesseur sont :

(depuis C++20)
Le programme est mal formé si le caractère correspondant à cette catégorie est

Nombres de prétraitement

L'ensemble des jetons de prétraitement d'un nombre de prétraitement est un sur-ensemble de l'union des ensembles de jetons des littéraux entiers et des littéraux à virgule flottante :

. (facultatif) digit pp-continue-seq  (facultatif)
digit - un des chiffres 0-9
pp-continue-seq - une séquence de pp-continue s

Chaque pp-continue est l'un des éléments suivants :

identifier-continue (1)
exp-char sign-char (2)
. (3)
digit (4) (depuis C++14)
nondigit (5) (depuis C++14)
identifier-continue - tout caractère non initial d'un identifiant valide
exp-char - l'un des caractères P , p , (depuis C++11) E et e
sign-char - l'un des caractères + et -
digit - l'un des chiffres 0-9
nondigit - l'une des lettres latines A/a-Z/z et le tiret bas

Un nombre de prétraitement n'a pas de type ni de valeur ; il acquiert les deux après une conversion réussie en un jeton littéral entier/flottant.

Espace blanc

Espace blanc consiste en commentaires , caractères d'espacement, ou les deux.

Les caractères suivants sont des caractères d'espacement :

  • tabulation de caractère (U+0009)
  • saut de ligne / caractère de nouvelle ligne (U+000A)
  • tabulation de ligne (U+000B)
  • saut de page (U+000C)
  • espace (U+0020)

Les espaces blancs sont généralement utilisés pour séparer les jetons de préprocesseur, avec les exceptions suivantes :

  • Il ne s'agit pas d'un séparateur dans le nom d'en-tête, le littéral de caractère et le littéral de chaîne.
  • Les jetons de préprocessing séparés par des espaces blancs contenant des caractères de nouvelle ligne ne peuvent pas former des directives de préprocessing .
#include "my header"        // OK, utilisation d'un nom d'en-tête contenant des espaces
#include/*hello*/<iostream> // OK, utilisation d'un commentaire comme espace blanc
#include
<iostream> // Erreur : #include ne peut pas s'étendre sur plusieurs lignes
"str ing"  // OK, un seul jeton de prétraitement (littéral de chaîne)
' '        // OK, un seul jeton de prétraitement (littéral de caractère)

Maximal munch

Le maximal munch est la règle utilisée dans la phase 3 lors de la décomposition du fichier source en tokens de préprocesseur.

Si l'entrée a été analysée en jetons de prétraitement jusqu'à un caractère donné (sinon, le prochain jeton de prétraitement ne sera pas analysé, ce qui rend l'ordre d'analyse unique), le prochain jeton de prétraitement est généralement pris comme étant la plus longue séquence de caractères qui pourrait constituer un jeton de prétraitement, même si cela devait entraîner l'échec d'une analyse ultérieure. Ceci est communément appelé maximal munch .

int foo = 1;
int bar = 0xE+foo;   // Erreur : nombre de prétraitement invalide 0xE+foo
int baz = 0xE + foo; // OK

En d'autres termes, la règle du « maximal munch » favorise les opérateurs et ponctuateurs multi-caractères :

int foo = 1;
int bar = 2;
int num1 = foo+++++bar; // Erreur : traité comme "foo++ ++ +baz", pas "foo++ + ++baz"
int num2 = -----foo;    // Erreur : traité comme "-- -- -foo", pas "- -- --foo"

La règle du maximal munch comporte les exceptions suivantes :

  • Les jetons de prétraitement de nom d'en-tête ne sont formés que dans les cas suivants :
  • après le include jeton de préprocesseur dans une directive #include
(depuis C++17)
  • après le jeton de préprocesseur import dans une directive import
(depuis C++20)
std::vector<int> x; // OK, “int” n'est pas un nom d'en-tête
  • Si les trois caractères suivants sont < :: et que le caractère subséquent n'est ni : ni > , le < est traité comme un jeton de prétraitement autonome plutôt que comme le premier caractère du jeton alternatif < :: .
struct Foo { static const int v = 1; };
std::vector<::Foo> x;  // OK, <: n'est pas interprété comme le jeton alternatif pour [
extern int y<::>;      // OK, identique à “extern int y[];”
int z<:::Foo::value:>; // OK, identique à “int z[::Foo::value];”
  • Si les deux caractères suivants sont >> et que l'un des caractères > peut compléter un identifiant de template , le caractère est traité comme un jeton de prétraitement seul au lieu de faire partie du jeton de prétraitement >> .
template<int i> class X { /* ... */ };
template<class T> class Y { /* ... */ };
Y<X<1>> x3;      // OK, déclare une variable « x3 » de type « Y<X<1> > »
Y<X<6>>1>> x4;   // Erreur de syntaxe
Y<X<(6>>1)>> x5; // OK
  • Si le caractère suivant commence une séquence de caractères qui pourrait être le préfixe et le guillemet double initial d'un littéral de chaîne brute , le jeton de prétraitement suivant est un littéral de chaîne brute. Le littéral consiste en la plus courte séquence de caractères correspondant au motif de chaîne brute.
#define R "x"
const char* s = R"y";         // littéral de chaîne brute mal formé, pas "x" "y"
const char* s2 = R"(a)" "b)"; // un littéral de chaîne brute suivi d'un littéral de chaîne normale
(depuis C++11)

Jetons

Un token est l'élément lexical minimal du langage dans la phase de traduction 7.

Les catégories de token sont :

Phases de traduction

La traduction est effectuée as if dans l'ordre de la phase 1 à la phase 9. Les implémentations se comportent comme si ces phases distinctes se produisaient, bien qu'en pratique différentes phases puissent être fusionnées.

Phase 1 : Mappage des caractères source

1) Les octets individuels du fichier source sont mappés (de manière définie par l'implémentation) aux caractères du jeu de caractères source de base . En particulier, les indicateurs de fin de ligne dépendants du système d'exploitation sont remplacés par des caractères de nouvelle ligne.
2) L'ensemble des caractères de fichier source acceptés est défini par l'implémentation (depuis C++11) . Tout caractère de fichier source qui ne peut pas être mappé à un caractère du jeu de caractères source de base est remplacé par son nom de caractère universel (échappé avec \u ou \U ) ou par une forme définie par l'implémentation qui est traitée de manière équivalente.
3) Les séquences de trigraphes sont remplacées par les représentations à caractère unique correspondantes.
(jusqu'à C++17)
(jusqu'à C++23)

Les fichiers d'entrée qui sont une séquence d'unités de code UTF-8 (fichiers UTF-8) sont garantis d'être pris en charge. L'ensemble des autres types de fichiers d'entrée pris en charge est défini par l'implémentation. Si l'ensemble n'est pas vide, le type d'un fichier d'entrée est déterminé de manière définie par l'implémentation qui inclut un moyen de désigner les fichiers d'entrée comme fichiers UTF-8, indépendamment de leur contenu (la reconnaissance du marqueur d'ordre des octets n'est pas suffisante).

  • Si un fichier d'entrée est déterminé comme étant un fichier UTF-8, alors il doit être une séquence d'unités de code UTF-8 bien formée et il est décodé pour produire une séquence de valeurs scalaires Unicode. Une séquence d'éléments du jeu de caractères de traduction est ensuite formée en mappant chaque valeur scalaire Unicode à l'élément correspondant du jeu de caractères de traduction. Dans la séquence résultante, chaque paire de caractères dans la séquence d'entrée consistant en un retour chariot (U+000D) suivi d'un saut de ligne (U+000A), ainsi que chaque retour chariot (U+000D) non immédiatement suivi d'un saut de ligne (U+000A), est remplacée par un seul caractère de nouvelle ligne.
  • Pour tout autre type de fichier d'entrée pris en charge par l'implémentation, les caractères sont mappés (de manière définie par l'implémentation) à une séquence d'éléments du jeu de caractères de traduction. En particulier, les indicateurs de fin de ligne dépendants du système d'exploitation sont remplacés par des caractères de nouvelle ligne.
(depuis C++23)

Phase 2 : Éclissage des lignes

1) Si le premier caractère de traduction est la marque d'ordre des octets (U+FEFF), il est supprimé. (depuis C++23) Chaque fois qu'une barre oblique inverse ( \ ) apparaît à la fin d'une ligne (immédiatement suivie par zéro ou plusieurs caractères d'espacement autres que le saut de ligne suivis par (depuis C++23) le caractère de saut de ligne), ces caractères sont supprimés, combinant deux lignes physiques de source en une seule ligne logique de source. Ceci est une opération à passage unique ; une ligne se terminant par deux barres obliques inverses suivie d'une ligne vide ne combine pas trois lignes en une.
2) Si un fichier source non vide ne se termine pas par un caractère de nouvelle ligne après cette étape (les barres obliques inverses de fin de ligne ne sont plus des jonctions à ce stade), un caractère de nouvelle ligne de terminaison est ajouté.

Phase 3 : Lexicalisation

1) Le fichier source est décomposé en jetons de prétraitement et espaces blancs :
// The following #include directive can de decomposed into 5 preprocessing tokens:
//     punctuators (#, < and >)
//          │
// ┌────────┼────────┐
// │        │        │
   #include <iostream>
//     │        │
//     │        └── header name (iostream)
//     │
//     └─────────── identifier (include)
Si un fichier source se termine par un jeton de prétraitement partiel ou par un commentaire partiel, le programme est mal formé :
// Error: partial string literal
"abc
// Error: partial comment
/* comment
Lorsque les caractères du fichier source sont consommés pour former le prochain jeton de préprocesseur (c'est-à-dire qu'ils ne sont pas consommés dans le cadre d'un commentaire ou d'autres formes d'espace blanc), les noms de caractères universels sont reconnus et remplacés par l'élément désigné du jeu de caractères de traduction , sauf lors de la correspondance d'une séquence de caractères dans l'un des jetons de préprocesseur suivants :
  • un littéral de caractère ( c-char-sequence )
  • un littéral de chaîne ( s-char-sequence et r-char-sequence ), à l'exclusion des délimiteurs ( d-char-sequence )
  • un nom d'en-tête ( h-char-sequence et q-char-sequence )
(depuis C++23)


2) Toutes les transformations effectuées durant la phase 1 et (jusqu'à C++23) la phase 2 entre les guillemets doubles initiaux et finaux de tout littéral de chaîne brut sont annulées.
(depuis C++11)
3) L'espace blanc est transformé :
  • Chaque commentaire est remplacé par un caractère d'espace.
  • Les caractères de nouvelle ligne sont conservés.
  • Le fait que chaque séquence non vide de caractères d'espace blanc autres que la nouvelle ligne soit conservée ou remplacée par un caractère d'espace n'est pas spécifié.

Phase 4 : Prétraitement

1) Le preprocessor est exécuté.
2) Chaque fichier introduit avec la directive #include passe par les phases 1 à 4, de manière récursive.
3) À la fin de cette phase, toutes les directives du préprocesseur sont supprimées de la source.

Phase 5 : Détermination des encodages courants des littéraux de chaîne

1) Tous les caractères dans les littéraux de caractères et les littéraux de chaîne sont convertis du jeu de caractères source vers l' encodage (qui peut être un encodage de caractères multi-octets tel que UTF-8, tant que les 96 caractères du jeu de caractères de base ont des représentations en un seul octet).
2) Les séquences d'échappement et les noms de caractères universels dans les littéraux de caractères et les littéraux de chaîne non bruts sont développés et convertis vers l'encodage du littéral.

Si le caractère spécifié par un nom de caractère universel ne peut pas être encodé comme un seul point de code dans l'encodage de littéral correspondant, le résultat est défini par l'implémentation, mais il est garanti de ne pas être un caractère (large) nul.

(jusqu'en C++23)

Pour une séquence de deux ou plusieurs jetons de littéral de chaîne adjacents, un préfixe d'encodage commun est déterminé comme décrit ici . Chaque jeton de littéral de chaîne est alors considéré comme ayant ce préfixe d'encodage commun. (La conversion des caractères est déplacée vers la phase 3)

(depuis C++23)

Phase 6 : Concaténation des littéraux de chaîne

Les littéraux de chaîne adjacents sont concaténés.

Phase 7 : Compilation

La compilation a lieu : chaque jeton de prétraitement est converti en un jeton . Les jetons sont analysés syntaxiquement et sémantiquement, puis traduits en tant qu' unité de traduction .

Phase 8 : Instanciation des templates

Chaque unité de traduction est examinée pour produire une liste d'instanciations de modèles requises, y compris celles demandées par les instanciations explicites . Les définitions des modèles sont localisées, et les instanciations requises sont effectuées pour produire des unités d'instanciation .

Phase 9 : Édition des liens

Les unités de traduction, les unités d'instanciation et les composants de bibliothèque nécessaires pour satisfaire les références externes sont rassemblés en une image de programme qui contient les informations nécessaires à l'exécution dans son environnement d'exécution.

Notes

Les fichiers sources, les unités de traduction et les unités de traduction traduites ne doivent pas nécessairement être stockés sous forme de fichiers, et il n'est pas nécessaire qu'il y ait une correspondance un-à-un entre ces entités et toute représentation externe. La description est uniquement conceptuelle et ne spécifie aucune implémentation particulière.

La conversion effectuée lors de la phase 5 peut être contrôlée par des options de ligne de commande dans certaines implémentations : gcc et clang utilisent - finput - charset pour spécifier l'encodage du jeu de caractères source, - fexec - charset et - fwide - exec - charset pour spécifier respectivement les encodages des littéraux ordinaires et larges, tandis que Visual Studio 2015 Update 2 et ultérieur utilise / source - charset et / execution - charset pour spécifier respectivement le jeu de caractères source et l'encodage des littéraux.

(jusqu'à C++23)

Certains compilateurs n'implémentent pas les unités d'instanciation (également appelées template repositories ou template registries ) et compilent simplement chaque instanciation de template à la phase 7, stockant le code dans le fichier objet où elle est implicitement ou explicitement demandée, puis l'éditeur de liens fusionne ces instanciations compilées en une seule à la phase 9.

Rapports de défauts

Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.

DR Applicable à Comportement publié Comportement corrigé
CWG 787 C++98 le comportement était indéfini si un fichier source non vide ne se
terminait pas par un caractère de nouvelle ligne à la fin de la phase 2
ajouter un caractère de nouvelle ligne terminal
dans ce cas
CWG 1104 C++98 le jeton alternatif < : provoquait std:: vector < :: std:: string >
d'être traité comme std:: vector [ : std:: string >
ajout d'une règle d'analyse lexicale
supplémentaire pour traiter ce cas
CWG 1775 C++11 la formation d'un nom de caractère universel à l'intérieur d'un littéral
de chaîne brut en phase 2 résultait en un comportement indéfini
rendu bien défini
CWG 2747 C++98 la phase 2 vérifiait la jonction de fin de fichier après le collage, ce qui est inutile suppression de la vérification
P2621R3 C++98 les noms de caractères universels n'étaient pas autorisés à
être formés par collage de lignes ou concaténation de jetons
autorisé

Références

  • Norme C++23 (ISO/CEI 14882:2024) :
  • 5.2 Phases de traduction [lex.phases]
  • Norme C++20 (ISO/CEI 14882:2020) :
  • 5.2 Phases de traduction [lex.phases]
  • Norme C++17 (ISO/CEI 14882:2017) :
  • 5.2 Phases de traduction [lex.phases]
  • Norme C++14 (ISO/CEI 14882:2014) :
  • 2.2 Phases de la traduction [lex.phases]
  • Norme C++11 (ISO/CEI 14882:2011) :
  • 2.2 Phases de traduction [lex.phases]
  • Norme C++03 (ISO/CEI 14882:2003) :
  • 2.1 Phases de traduction [lex.phases]
  • Norme C++98 (ISO/CEI 14882:1998) :
  • 2.1 Phases de traduction [lex.phases]

Voir aussi

Documentation C pour Phases de traduction