翻訳フェーズ
C++ ソースファイルはコンパイラによって処理され、C++ プログラムが生成されます。
目次 |
[編集] 翻訳プロセス
C++ プログラムのテキストは、ソースファイル と呼ばれる単位で保持されます。
C++ ソースファイルは、以下のステップで翻訳されて翻訳単位になります。
- 各ソースファイルを文字シーケンスにマッピングします。
- 各文字シーケンスを、空白文字で区切られたプリプロセッサトークンのシーケンスに変換します。
- 各プリプロセッサトークンをトークンに変換し、トークンシーケンスを形成します。
- 各トークンシーケンスを翻訳単位に変換します。
C++ プログラムは、翻訳された翻訳単位から構成できます。翻訳単位は個別に翻訳され、後でリンクして実行可能プログラムを生成できます。
上記のプロセスは、9 つの翻訳フェーズに整理できます。
[編集] プリプロセッサトークン
プリプロセッサトークンは、フェーズ 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 | - | E、e、P、p(C++11 以降) のいずれか |
| sign-char | - | +、- のいずれか |
| digit | - | 0 から 9 までの数字 |
| nondigit | - | ラテン文字 A/a-Z/z およびアンダースコアのいずれか |
プリプロセッサ数値には型も値もありません。これらは、整数/浮動小数点リテラルトークンへの正常な変換後に、型と値を取得します。
[編集] 空白文字
空白文字は、コメント、空白文字、またはその両方で構成されます。
次の文字が空白文字です。
- 文字タブ (U+0009)
- 改行 / 新しい行の文字 (U+000A)
- 行タブ (U+000B)
- フォームフィード (U+000C)
- スペース (U+0020)
空白文字は通常、プリプロセッサトークンを区切るために使用されますが、例外があります。
- ヘッダー名、文字リテラル、文字列リテラル内では区切り文字にはなりません。
- 改行文字を含む空白文字で区切られたプリプロセッサトークンは、プリプロセスディレクティブを形成できません。
#include "my header" // OK, using a header name containing whitespace #include/*hello*/<iostream> // OK, using a comment as whitespace #include <iostream> // Error: #include cannot span across multiple lines "str ing" // OK, a single preprocessing token (string literal) ' ' // OK, a single preprocessing token (character literal)
[編集] 最大マッチ
入力が特定の文字までプリプロセッサトークンに解析された場合、次のプリプロセッサトークンは、一般的にプリプロセッサトークンを構成できる最長の文字シーケンスとして取得されます。これは、後続の解析に失敗する可能性があっても同様です。これは一般に最大マッチとして知られています。
int foo = 1; int bar = 0xE+foo; // Error: invalid preprocessing number 0xE+foo int baz = 0xE + foo; // OK
つまり、最大マッチ規則は、複数文字演算子および句読点を優先します。
int foo = 1; int bar = 2; int num1 = foo+++++bar; // Error: treated as “foo++ ++ +baz”, not “foo++ + ++baz” int num2 = -----foo; // Error: treated as “-- -- -foo”, not “- -- --foo”
最大マッチ規則には、次の例外があります。
- ヘッダー名プリプロセッサトークンは、次の場合にのみ形成されます。
- #include ディレクティブの include プリプロセッサトークンの後。
|
(C++17以降) |
|
(C++20以降) |
std::vector<int> x; // OK, “int” is not a header name
- 次の 3 文字が <:: で、後続の文字が : でも > でもない場合、< は、代替トークン <: の最初の文字としてではなく、単独のプリプロセッサトークンとして扱われます。
struct Foo { static const int v = 1; }; std::vector<::Foo> x; // OK, <: not taken as the alternative token for [ extern int y<::>; // OK, same as “extern int y[];” int z<:::Foo::value:>; // OK, same as “int z[::Foo::value];”
template<int i> class X { /* ... */ }; template<class T> class Y { /* ... */ }; Y<X<1>> x3; // OK, declares a variable “x3” of type “Y<X<1> >” Y<X<6>>1>> x4; // Syntax error Y<X<(6>>1)>> x5; // OK
#define R "x" const char* s = R"y"; // ill-formed raw string literal, not "x" "y" const char* s2 = R"(a)" "b)"; // a raw string literal followed by a normal string literal |
(C++11以降) |
[編集] トークン
トークンは、フェーズ 7 における言語の最小の字句要素です。
トークンのカテゴリは次のとおりです。
[編集] 翻訳フェーズ
翻訳は、フェーズ 1 からフェーズ 9 までの順序で、あたかも行われるように実行されます。実装は、これらの個別のフェーズが発生するかのように動作しますが、実際には異なるフェーズがまとめられることがあります。
[編集] フェーズ 1: ソース文字のマッピング
|
1) ソースコードファイルの個々のバイトは、(実装定義の方法で)基本ソース文字セットの文字にマッピングされます。特に、OS依存の行末インジケータは改行文字に置き換えられます。
2) 受け入れられるソースファイル文字のセットは実装定義です(C++11 以降)。基本ソース文字セットの文字にマッピングできないソースファイル文字は、そのユニバーサル文字名(
\u または \U でエスケープ)または同等に扱われる実装定義の形式に置き換えられます。
|
(C++23まで) | ||
|
UTF-8 コードユニットのシーケンス(UTF-8 ファイル)である入力ファイルは、サポートされることが保証されています。サポートされるその他の種類の入力ファイルは実装定義です。セットが空でない場合、入力ファイルの種別は、入力ファイルを UTF-8 ファイルとして指定する手段を含む実装定義の方法で決定されます(バイトオーダーマークを認識するだけでは不十分です)。
|
(C++23から) |
[編集] フェーズ 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から) |
|
2) フェーズ 1 および(C++23 まで) フェーズ 2 で、生の文字列リテラルの最初の二重引用符と最後の二重引用符の間で行われた変換は元に戻されます。
|
(C++11以降) |
- 各コメントは、1 つのスペース文字に置き換えられます。
- 改行文字は保持されます。
- 改行以外の各非空の空白文字シーケンスが保持されるか、1 つのスペース文字に置き換えられるかは未指定です。
[編集] フェーズ 4: プリプロセス
[編集] フェーズ 5: 一般的な文字列リテラルのエンコーディングの決定
|
1) 文字リテラルおよび文字列リテラルのすべての文字は、ソース文字セットからエンコーディング(UTF-8 のようなマルチバイト文字エンコーディングであってもよい。ただし、基本文字セットの 96 文字が単一バイト表現を持つ限り)に変換されます。
2) 文字リテラルおよび通常の文字列リテラル内のエスケープシーケンスとユニバーサル文字名は展開され、リテラルのエンコーディングに変換されます。 ユニバーサル文字名によって指定された文字が、対応するリテラルエンコーディングで単一のコードポイントとしてエンコードできない場合、結果は実装定義ですが、ヌル(ワイド)文字ではないことが保証されます。 |
(C++23まで) |
|
2 つ以上の隣接する文字列リテラルトークンのシーケンスについては、共通のエンコーディングプレフィックスがここで説明されているように決定されます。その後、各文字列リテラルトークンはその共通のエンコーディングプレフィックスを持つと見なされます。(文字変換はフェーズ 3 に移動します) |
(C++23から) |
[編集] フェーズ 6: 文字列リテラルの連結
隣接する文字列リテラルが連結されます。
[編集] フェーズ 7: コンパイル
コンパイルが行われます。各プリプロセッサトークンはトークンに変換されます。トークンは構文的および意味的に解析され、翻訳単位として翻訳されます。
[編集] フェーズ 8: テンプレートのインスタンス化
各翻訳単位は、明示的なインスタンス化によって要求されたものを含む、必要なテンプレートインスタンス化のリストを生成するために検査されます。テンプレートの定義が見つけられ、必要なインスタンス化が実行されてインスタンス化単位が生成されます。
[編集] フェーズ 9: リンク
翻訳単位、インスタンス化単位、および外部参照を満たすために必要なライブラリコンポーネントが、実行環境での実行に必要な情報を含むプログラムイメージに収集されます。
[編集] 注記
ソースファイル、翻訳単位、および翻訳された翻訳単位は、必ずしもファイルとして格納される必要はなく、これらのエンティティと外部表現との間に 1 対 1 の対応がある必要もありません。説明は概念的なものであり、特定の実際の実装を指定するものではありません。
|
フェーズ 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 Phases of translation [lex.phases]
- C++20 standard (ISO/IEC 14882:2020)
- 5.2 Phases of translation [lex.phases]
- C++17 standard (ISO/IEC 14882:2017)
- 5.2 Phases of translation [lex.phases]
- C++14 standard (ISO/IEC 14882:2014)
- 2.2 Phases of translation [lex.phases]
- C++11 standard (ISO/IEC 14882:2011)
- 2.2 Phases of translation [lex.phases]
- C++03 標準 (ISO/IEC 14882:2003)
- 2.1 Phases of translation [lex.phases]
- C++98 標準 (ISO/IEC 14882:1998)
- 2.1 Phases of translation [lex.phases]
[編集] 関連項目
| C のドキュメント (翻訳フェーズ)
|