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++プログラムのテキストは、 source files と呼ばれる単位で保持されます。

C++ソースファイルは以下のステップを経て 翻訳 され、 翻訳単位 となります:

  1. 各ソースファイルを文字シーケンスにマッピングします。
  2. 各文字シーケンスを、空白で区切られたプリプロセストークンシーケンスに変換します。
  3. 各プリプロセストークンをトークンに変換し、トークンシーケンスを形成します。
  4. 各トークンシーケンスを翻訳単位に変換します。

C++プログラムは、翻訳された翻訳単位から形成することができます。翻訳された翻訳単位とインスタンス化された単位(インスタンス化された単位は以下のフェーズ8で説明されます)は、個別に保存するか、ライブラリに保存することができます。複数の翻訳単位は、(例えば)外部リンケージを持つシンボルやデータファイルを通じて互いに通信します。翻訳単位は個別に翻訳した後、後でリンクして実行可能プログラムを生成することができます。

上記のプロセスは9つの translation phases に整理されます。

プリプロセストークン

プリプロセッシングトークンは、翻訳フェーズ3から6における言語の最小字句要素です。

前処理トークンのカテゴリは以下の通りです:

(C++20以降)
このカテゴリに該当する文字が以下の場合、プログラムは不適格となります:
  • アポストロフィ( ' , U+0027)、
  • 引用符( " , U+0022)、または
  • 基本文字集合 に含まれない文字である場合。

プリプロセス数

前処理数値の前処理トークンの集合は、 整数リテラル 浮動小数点リテラル のトークン集合の和集合の上位集合です:

. (オプション) 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 , (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

言い換えれば、最大マンチ規則は 複数文字の演算子と区切り文字 を優先します:

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」はヘッダー名ではない
  • 次の3文字が < :: であり、かつ後続の文字が : でも > でもない場合、 < 代替トークン < : の最初の文字としてではなく、単独の前処理トークンとして扱われます。
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];」と同じ
  • 次の2文字が >> であり、かつ > 文字の1つが テンプレート識別子 を完成させることができる場合、その文字は前処理トークン >> の一部ではなく、単独の前処理トークンとして扱われる。
template<int i> class X { /* ... */ };
template<class T> class Y { /* ... */ };
Y<X<1>> x3;      // OK, 型 "Y<X<1> >" の変数 "x3" を宣言
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以降)

トークン

トークンは、翻訳フェーズ7における言語の最小字句要素です。

トークンのカテゴリは以下の通りです:

翻訳フェーズ

翻訳はフェーズ1からフェーズ9への順序で as if 行われます。実装はこれらの個別のフェーズが発生するかのように動作しますが、実際には異なるフェーズがまとめて実行される場合があります。

フェーズ1: ソース文字のマッピング

1) ソースコードファイルの個々のバイトは(実装定義の方法で) 基本ソース文字集合 の文字にマッピングされる。特に、OS依存の行末指示子は改行文字で置き換えられる。
2) 受け入れられるソースファイル文字の集合は実装定義である (C++11以降) 基本ソース文字集合 の文字にマッピングできないソースファイル文字は、その ユニバーサル文字名 \u または \U でエスケープされたもの)または同等に扱われる実装定義の形式で置き換えられる。
3) Trigraph sequences は対応する単一文字表現で置き換えられる。
(C++17まで)
(C++23まで)

UTF-8コード単位のシーケンスである入力ファイル(UTF-8ファイル)はサポートが保証される。その他のサポートされる入力ファイルの種類の集合は実装定義である。集合が空でない場合、入力ファイルの種類は、その内容に関係なく入力ファイルをUTF-8ファイルとして指定する手段を含む実装定義の方法で決定される(バイト順マークの認識だけでは不十分である)。

  • 入力ファイルがUTF-8ファイルと判定された場合、それは整形式のUTF-8コード単位シーケンスでなければならず、Unicodeスカラー値のシーケンスを生成するためにデコードされる。 翻訳文字集合 要素のシーケンスは、各Unicodeスカラー値を対応する翻訳文字集合要素にマッピングすることで形成される。結果のシーケンスにおいて、キャリッジリターン(U+000D)にラインフィード(U+000A)が続く入力シーケンスの文字ペア、および直後にラインフィード(U+000A)が続かない各キャリッジリターン(U+000D)は、単一の改行文字で置き換えられる。
  • 実装によってサポートされる他の種類の入力ファイルについては、文字は(実装定義の方法で)翻訳文字集合要素のシーケンスにマッピングされる。特に、OS依存の行末指示子は改行文字で置き換えられる。
(C++23以降)

Phase 2: ラインのスプライシング

1) 最初の変換文字がバイト順マーク (U+FEFF) の場合、それは削除されます。 (since C++23) バックスラッシュ ( \ ) が行末に現れる場合(直後に 改行以外の空白文字が0個以上続き、 (since C++23) その後に改行文字が続く)、これらの文字は削除され、2つの物理ソース行が1つの論理ソース行に結合されます。これは単一パス操作です。2つのバックスラッシュで終わる行の後に空行が続く場合でも、3行を1行に結合することはありません。
2) 空でないソースファイルがこのステップの後(この時点では行末のバックスラッシュはもはやスプライスではありません)に改行文字で終わっていない場合、終端の改行文字が追加されます。

フェーズ3: 字句解析

1) ソースファイルは プリプロセッシングトークン 空白文字 に分解されます:
// 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および フェーズ2で実行された変換のうち、 (C++23まで) 任意の 生文字列リテラル の最初と最後の二重引用符の間で行われたものは元に戻される。
(C++11以降)
3) 空白文字は変換されます:
  • 各コメントは1つのスペース文字に置き換えられます。
  • 改行文字は保持されます。
  • 改行以外の空白文字の空でないシーケンスが保持されるか、1つのスペース文字に置き換えられるかは未規定です。

(注:指定されたテキスト部分に翻訳対象となる自然言語のテキストが含まれていないため、HTML構造はそのまま保持されています)

フェーズ4: プリプロセス

1) プリプロセッサが実行されます。
2) #include ディレクティブで導入される各ファイルは、フェーズ1から4を再帰的に通過します。
3) このフェーズの終了時点で、すべてのプリプロセッサディレクティブがソースから削除されます。

Phase 5: 共通文字列リテラルエンコーディングの決定

1) 文字リテラルと文字列リテラル内のすべての文字は、ソース文字セットから 文字リテラル および 文字列リテラル エンコーディング に変換されます(これはUTF-8のようなマルチバイト文字エンコーディングであっても構いませんが、 基本文字セット の96文字は単一バイト表現を持つ必要があります)。
2) エスケープシーケンス および文字リテラルと非生文字列リテラル内のユニバーサル文字名は展開され、リテラルエンコーディングに変換されます。

ユニバーサル文字名によって指定された文字が、対応するリテラルエンコーディングで単一のコードポイントとしてエンコードできない場合、結果は実装定義ですが、null(ワイド)文字ではないことが保証されます。

(C++23まで)

2つ以上の隣接する 文字列リテラル トークンのシーケンスに対して、共通のエンコーディングプレフィックスが ここ で説明されているように決定されます。その後、そのような各文字列リテラルトークンは、その共通のエンコーディングプレフィックスを持つものと見なされます。 (文字変換はフェーズ3に移動されました)

(C++23以降)

(注:指定されたテキストブロック内に翻訳対象となる自然言語のテキストが存在しないため、HTML構造はそのまま保持されています)

Phase 6: 文字列リテラルの連結

隣接する string literals は連結されます。

Phase 7: コンパイル

コンパイルが実行される: 各プリプロセッシングトークンは token に変換される。これらのトークンは構文的・意味的に解析され、 translation unit として翻訳される。

(注:指定されたテキストには翻訳対象となる自然言語のテキストが含まれていないため、HTML構造はそのまま保持されています)

フェーズ8: テンプレートのインスタンス化

各翻訳単位は、必要なテンプレートのインスタンス化のリストを生成するために検査されます。これには 明示的インスタンス化 によって要求されるものも含まれます。テンプレートの定義が特定され、必要なインスタンス化が実行されて インスタンス化単位 が生成されます。

(注:指定されたテキスト部分には翻訳対象となる自然言語のテキストが含まれていないため、HTML構造のみが保持されています)

Phase 9: リンク

外部参照を満たすために必要な翻訳単位、インスタンス化単位、およびライブラリコンポーネントは、その実行環境での実行に必要な情報を含むプログラムイメージに収集されます。

注記

ソースファイル、翻訳単位、および翻訳済み翻訳単位は、必ずしもファイルとして保存される必要はなく、これらの実体と外部表現との間に一対一の対応関係がある必要もありません。この記述は概念的なものであり、特定の実装を規定するものではありません。

フェーズ5で実行される変換は、一部の実装ではコマンドラインオプションによって制御できます:gccとclangは - finput - charset を使用してソース文字セットのエンコーディングを指定し、 - fexec - charset および - fwide - exec - charset を使用してそれぞれ通常リテラルとワイドリテラルのエンコーディングを指定します。一方、Visual Studio 2015 Update 2以降では / source - charset および / execution - charset を使用して、それぞれソース文字セットとリテラルエンコーディングを指定します。

(C++23まで)

一部のコンパイラはインスタンス化単位( テンプレートリポジトリ または テンプレートレジストリ とも呼ばれる)を実装しておらず、単純にフェーズ7で各テンプレートインスタンス化をコンパイルし、暗黙的または明示的に要求されたオブジェクトファイルにコードを格納し、その後リンカーがフェーズ9でこれらのコンパイルされたインスタンス化を1つに統合します。

不具合報告

以下の動作変更の欠陥報告書は、以前に公開された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でスプライシング後にEOFスプライスをチェックしていたが、これは不要 チェックを削除
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 for Phases of translation
日本語訳:
C documentation for Phases of translation
翻訳結果: - "C documentation for Phases of translation" → "翻訳フェーズのCドキュメント" - HTMLタグ、属性、コード内のテキストは翻訳せず保持 - C++専門用語は翻訳せず保持