Phases of translation
C++ソースファイルは、コンパイラによって処理され、C++プログラムが生成されます。
目次 |
翻訳プロセス
C++プログラムのテキストは、 source files と呼ばれる単位で保持されます。
C++ソースファイルは以下のステップを経て 翻訳 され、 翻訳単位 となります:
- 各ソースファイルを文字シーケンスにマッピングします。
- 各文字シーケンスを、空白で区切られたプリプロセストークンシーケンスに変換します。
- 各プリプロセストークンをトークンに変換し、トークンシーケンスを形成します。
- 各トークンシーケンスを翻訳単位に変換します。
C++プログラムは、翻訳された翻訳単位から形成することができます。翻訳された翻訳単位とインスタンス化された単位(インスタンス化された単位は以下のフェーズ8で説明されます)は、個別に保存するか、ライブラリに保存することができます。複数の翻訳単位は、(例えば)外部リンケージを持つシンボルやデータファイルを通じて互いに通信します。翻訳単位は個別に翻訳した後、後でリンクして実行可能プログラムを生成することができます。
上記のプロセスは9つの translation phases に整理されます。
プリプロセストークン
プリプロセッシングトークンは、翻訳フェーズ3から6における言語の最小字句要素です。
前処理トークンのカテゴリは以下の通りです:
- ヘッダー名 (例: < iostream > または "myfile.h" )
|
(C++20以降) |
- 識別子
- 前処理数値(下記参照)
- 文字リテラル 、 ユーザー定義 文字リテラルを含む (C++11以降)
- 文字列リテラル 、 ユーザー定義 文字列リテラルを含む (C++11以降)
- 演算子と区切り文字 、 代替トークン を含む
- 他のカテゴリに該当しない個々の非空白文字
-
このカテゴリに該当する文字が以下の場合、プログラムは不適格となります:
- アポストロフィ( ' , 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以降) |
|
(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];」と同じ
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
でエスケープされたもの)または同等に扱われる実装定義の形式で置き換えられる。
|
(C++23まで) | ||
|
UTF-8コード単位のシーケンスである入力ファイル(UTF-8ファイル)はサポートが保証される。その他のサポートされる入力ファイルの種類の集合は実装定義である。集合が空でない場合、入力ファイルの種類は、その内容に関係なく入力ファイルをUTF-8ファイルとして指定する手段を含む実装定義の方法で決定される(バイト順マークの認識だけでは不十分である)。
|
(C++23以降) |
Phase 2: ラインのスプライシング
フェーズ3: 字句解析
// 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++23以降) |
| (C++11以降) |
- 各コメントは1つのスペース文字に置き換えられます。
- 改行文字は保持されます。
- 改行以外の空白文字の空でないシーケンスが保持されるか、1つのスペース文字に置き換えられるかは未規定です。
(注:指定されたテキスト部分に翻訳対象となる自然言語のテキストが含まれていないため、HTML構造はそのまま保持されています)
フェーズ4: プリプロセス
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
|