名前空間
変種
操作

翻訳フェーズ

From cppreference.com
< cpp‎ | language
 
 
C++言語
全般
フロー制御
条件実行文
if
繰り返し文 (ループ)
for
範囲for (C++11)
ジャンプ文
関数
関数宣言
ラムダ式
inline指定子
動的例外仕様 (C++17まで*)
noexcept指定子 (C++11)
例外
名前空間
指定子
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点数
文字 - 文字列 - nullptr (C++11)
ユーザー定義 (C++11)
ユーティリティ
属性 (C++11)
typedef宣言
型エイリアス宣言 (C++11)
キャスト
メモリ確保
クラス
クラス固有の関数プロパティ
explicit (C++11)
static

特殊メンバ関数
テンプレート
その他
 
 

C++ ソースファイルはコンパイラによって処理され、C++ プログラムが生成されます。

目次

[編集] 翻訳プロセス

C++ プログラムのテキストは、ソースファイル と呼ばれる単位で保持されます。

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

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

C++ プログラムは、翻訳された翻訳単位から構成できます。翻訳単位は個別に翻訳され、後でリンクして実行可能プログラムを生成できます。

上記のプロセスは、9 つの翻訳フェーズに整理できます。

[編集] プリプロセッサトークン

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

プリプロセッサトークンのカテゴリは次のとおりです。

(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 - EePp(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以降)
  • import ディレクティブの import プリプロセッサトークンの後。
(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];”
  • 次の 2 文字が >> で、> 文字のいずれかが テンプレート識別子を完了できる場合、その文字は >> プリプロセッサトークンの一部ではなく、単独のプリプロセッサトークンとして扱われます。
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 でエスケープ)または同等に扱われる実装定義の形式に置き換えられます。
3) トライグラフシーケンスは、対応する単一文字表現に置き換えられます。
(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から)

[編集] フェーズ 2: 行のマージ

1) 最初の翻訳文字がバイトオーダーマーク (U+FEFF) の場合、削除されます。(C++23 以降) 行末にバックスラッシュ (\) がある場合(直後に改行文字以外のゼロ個以上の空白文字が続く場合(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 および(C++23 まで) フェーズ 2 で、生の文字列リテラルの最初の二重引用符と最後の二重引用符の間で行われた変換は元に戻されます。
(C++11以降)
3) 空白文字は変換されます。
  • 各コメントは、1 つのスペース文字に置き換えられます。
  • 改行文字は保持されます。
  • 改行以外の各非空の空白文字シーケンスが保持されるか、1 つのスペース文字に置き換えられるかは未指定です。

[編集] フェーズ 4: プリプロセス

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

[編集] フェーズ 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 のドキュメント翻訳フェーズ
English 日本語 中文(简体) 中文(繁體)