Initialization
初期化 は、構築時に変数に初期値を与える処理です。
初期値は declarator の初期化子セクション、または new expression で提供される。また、関数呼び出し時にも発生する:関数パラメータと関数戻り値も初期化される。
目次 |
初期化子
各宣言子について、 initializer (存在する場合)は以下のいずれかになります:
=
式
|
(1) | ||||||||
= {}
= {
初期化子リスト
}
= {
指定初期化子リスト
}
|
(2) |
(C++20以降) |
|||||||
(
式リスト
)
(
初期化子リスト
)
|
(3) |
(C++11まで)
(C++11以降) |
|||||||
{}
{
初期化子リスト
}
{
指定初期化子リスト
}
|
(4) |
(C++11以降)
(C++11以降) (C++20以降) |
|||||||
| expression | - | 任意の式(括弧で囲まれていない コンマ式 を除く) |
| expression-list | - | コンマ区切りの式のリスト(括弧で囲まれていないコンマ式を除く) |
| initializer-list | - | コンマ区切りの初期化子句のリスト(下記参照) |
| designated-initializer-list | - | コンマ区切りの 指定初期化子句 のリスト |
初期化句(
initializer clause
)は以下のいずれかになります:
| expression | (1) | ||||||||
{}
|
(2) | ||||||||
{
initializer-list
}
|
(3) | ||||||||
{
designated-initializer-list
}
|
(4) | (C++20以降) | |||||||
構文 (2-4) は総称して brace-enclosed initializer list と呼ばれます。
初期化子のセマンティクス
オブジェクトに対して初期化子が指定されない場合、そのオブジェクトは デフォルト初期化 されます。 参照 に対して初期化子が指定されない場合、プログラムは不適格となります。
オブジェクトに指定された初期化子が ( ) (構文上の制約により宣言子では使用できない)の場合、そのオブジェクトは value-initialized されます。参照に指定された初期化子が ( ) の場合、プログラムは不適格となります。
初期化子のセマンティクスは以下の通りです:
- 初期化されるエンティティが参照の場合、 reference initialization を参照してください。
-
それ以外の場合、初期化されるエンティティはオブジェクトです。オブジェクトの型を
Tとすると:
-
- 初期化子が構文 (1) の場合、オブジェクトは copy-initialized されます。
|
(C++11まで) |
|
(C++11以降) |
-
- 初期化子が構文 (3) の場合、オブジェクトは direct-initialized されます。
#include <string> std::string s1; // デフォルト初期化 std::string s2(); // 初期化ではありません! // 実際にはパラメータなしでstd::stringを返す関数「s2」の宣言 std::string s3 = "hello"; // コピー初期化 std::string s4("hello"); // 直接初期化 std::string s5{'a'}; // リスト初期化 (C++11以降) char a[3] = {'a', 'b'}; // 集成体初期化 // (C++11以降はリスト初期化の一部) char& c = a[0]; // 参照初期化
非ローカル変数
静的 storage duration を持つすべての非ローカル変数は、プログラム起動時に、 main function の実行開始前に初期化されます(延期される場合を除く、下記参照)。スレッドローカルストレージ期間を持つすべての非ローカル変数は、スレッド起動時に、スレッド関数の実行開始前に初期化されます。これらの両方の変数カテゴリについて、初期化は2つの異なる段階で発生します:
静的初期化
静的初期化には2つの形式があります:
実際の使用例:
- 定数初期化は通常コンパイル時に行われます。事前に計算されたオブジェクト表現はプログラムイメージの一部として保存されます。コンパイラがこれを実行しない場合でも、動的初期化の前に初期化が行われることを保証しなければなりません。
-
ゼロ初期化される変数はプログラムイメージの
.bssセグメントに配置され、これはディスク上でスペースを占有せず、プログラムのロード時にOSによってゼロクリアされます。
動的初期化
すべての静的初期化が完了した後、非ローカル変数の動的初期化は以下の状況で発生します:
|
2)
部分的順序付けされた動的初期化
。これは、暗黙的または明示的にインスタンス化された特殊化ではないすべてのインライン変数に適用される。部分的に順序付けされたVが、すべての翻訳単位で順序付けされたまたは部分的に順序付けされたWよりも前に定義されている場合、Vの初期化はWの初期化よりも前にシーケンスされる(または、プログラムがスレッドを開始する場合は、前に発生する)。
|
(C++17以降) |
静的記憶域期間またはスレッド記憶域期間を持つ非ローカル変数の初期化が例外によって終了した場合、 std::terminate が呼び出されます。
早期動的初期化
コンパイラは、以下の両方の条件が満たされる場合、動的に初期化される変数を静的初期化の一部として(本質的にはコンパイル時に)初期化することが許可されています:
上記の規則により、あるオブジェクト
o1
の初期化が名前空間スコープのオブジェクト
o2
を参照する場合で、かつ
o2
が動的初期化を必要とする可能性があるが、同じ翻訳単位で後方に定義されているとき、
o2
の使用される値が完全に初期化された
o2
の値になるか(コンパイラが
o2
の初期化をコンパイル時に昇格させたため)、単にゼロ初期化された
o2
の値になるかは未規定です。
inline double fd() { return 1.0; } extern double d1; double d2 = d1; // 未規定: // d1が動的に初期化される場合は0.0に動的初期化、または // d1が静的に初期化される場合は1.0に動的初期化、または // 0.0に静的初期化(両変数が動的に初期化された場合の値になるため) double d1 = fd(); // 1.0に静的または動的に初期化される可能性がある
遅延動的初期化
動的初期化がmain関数の最初の文(静的変数の場合)またはスレッドの初期関数(スレッドローカルの場合)の前に発生するか、あるいは後まで延期されるかは実装定義である。
非インライン変数の初期化 (非インライン変数の初期化) (C++17以降) がmain/スレッド関数の最初の文の後まで遅延される場合、その初期化は、初期化対象の変数と同じ翻訳単位で定義された静的記憶域期間/スレッド記憶域期間を持つ任意の変数の最初の ODR-use の前に発生します。特定の翻訳単位から変数や関数がODR-usedされない場合、その翻訳単位で定義された非ローカル変数は初期化されない可能性があります(これはオンデマンド動的ライブラリの動作をモデル化しています)。ただし、翻訳単位から何かがODR-usedされる限り、初期化または破棄に副作用を持つすべての非ローカル変数は、プログラム内で使用されていない場合でも初期化されます。
|
インライン変数の初期化が遅延された場合、その特定の変数の最初の ODR-use の前に発生します。 |
(C++17以降) |
// ============ // == ファイル1 == #include "a.h" #include "b.h" B b; A::A() { b.Use(); } // ============ // == ファイル2 == #include "a.h" A a; // ============ // == ファイル3 == #include "a.h" #include "b.h" extern A a; extern B b; int main() { a.Use(); b.Use(); } // aがmain関数に入る前に初期化される場合、A::A()がbを使用する時点でbは未初期化の可能性がある // (翻訳単位間での動的初期化の順序は不定であるため) // aがmain関数の最初の文以降の時点で初期化される場合 // (ファイル1で定義された関数をodr-useすることで、その動的初期化が実行されることを強制する) // その場合、A::A内での使用前にbは初期化される
静的ローカル変数
ローカル(つまりブロックスコープ)の静的変数およびスレッドローカル変数の初期化については、 static block variables を参照してください。
初期化子は、ブロックスコープにおける 外部リンケージまたは内部リンケージ を持つ変数の宣言では許可されません。そのような宣言は extern と共に記述されなければならず、定義であってはなりません。
クラスメンバー
非静的データメンバーは、 member initializer list または default member initializer で初期化できます。
注記
非ローカル変数の破棄順序は std::exit で説明されています。
不具合報告
以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。
| DR | 適用バージョン | 公開時の仕様 | 修正後の仕様 |
|---|---|---|---|
| CWG 270 | C++98 |
クラステンプレートの静的データメンバの
初期化順序が未規定であった |
明示的特殊化と定義を除いて
順序不定と規定 |
| CWG 441 | C++98 |
静的記憶域期間を持つ非ローカル参照が
動的初期化前に常に初期化されるとは限らなかった |
静的初期化として扱い、常に
動的初期化前に初期化される |
| CWG 1415 | C++98 |
ブロックスコープの
extern
変数宣言が
定義となる場合があった |
禁止(そのような宣言では
初期化子を許可しない) |
| CWG 2599 | C++98 |
初期化子内での関数引数の評価が
初期化の一部か不明確であった |
初期化の一部である |
関連項目
|
C documentation
for
Initialization
|