名前空間
変種
操作

初期化

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

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

変数の初期化 (Initialization) は、構築時にその初期値を提供します。

初期値は、宣言子またはnew式の初期化子セクションで提供されます。これは関数呼び出し中にも行われます。つまり、関数パラメータと関数の戻り値も初期化されます。

目次

[編集] 初期化子

各宣言子に対して、初期化子 (initializer) が存在する場合、以下のいずれかになります。

= expression (1)
= {}
= { initializer-list }
= { designated-initializer-list }
(2)

(C++20以降)
( expression-list )
( initializer-list )
(3) (C++11まで)
(C++11以降)
{}
{ initializer-list }
{ designated-initializer-list }
(4) (C++11以降)
(C++11以降)
(C++20以降)
1) コピー初期化構文。
2) 集成体初期化構文。(C++11まで)リスト初期化構文。(C++11以降)
3) 直接初期化構文。
4) リスト初期化構文。
- 任意の式 (括弧で囲まれていないコンマ式を除く)
expression-list (式リスト) - コンマで区切られた式のリスト (括弧で囲まれていないコンマ式を除く)
initializer-list (初期化子リスト) - コンマで区切られた初期化子句 (initializer clause) のリスト (下記参照)
designated-initializer-list (指示付き初期化子リスト) - コンマで区切られた指示付き初期化子句のリスト


初期化子句 (initializer clause) は、以下のいずれかです。

(1)
{} (2)
{ initializer-list } (3)
{ designated-initializer-list } (4) (C++20以降)

構文 (2-4) は、まとめて波括弧で囲まれた初期化子リスト (brace-enclosed initializer list) と呼ばれます。

[編集] 初期化子のセマンティクス

オブジェクトに初期化子が指定されていない場合、そのオブジェクトはデフォルト初期化されます。参照に初期化子が指定されていない場合、プログラムは不適格 (ill-formed) となります。

オブジェクトに指定された初期化子が () (構文上の制約により宣言子には現れない) の場合、オブジェクトは値初期化されます。参照に指定された初期化子が () の場合、プログラムは不適格となります。

初期化子のセマンティクスは以下の通りです。

  • 初期化されるエンティティが参照である場合、参照の初期化を参照してください。
  • それ以外の場合、初期化されるエンティティはオブジェクトです。オブジェクトの型を T とすると、
  • 初期化子が構文 (1) の場合、オブジェクトはコピー初期化されます。
  • 初期化子が構文 (2) の場合、
  • T集成体である場合、集成体初期化が適用されます。
  • Tスカラ型である場合、T x = { a };T x = a; と等価です。
  • それ以外の場合、プログラムは不適格となります。
(C++11まで)
  • 初期化子が構文 (2) または (4) の場合、オブジェクトはリスト初期化されます。
(C++11以降)
  • 初期化子が構文 (3) の場合、オブジェクトは直接初期化されます。
#include <string>
 
std::string s1;           // default-initialization
std::string s2();         // NOT an initialization!
                          // actually declares a function “s2”
                          // with no parameter and returns std::string
std::string s3 = "hello"; // copy-initialization
std::string s4("hello");  // direct-initialization
std::string s5{'a'};      // list-initialization (since C++11)
 
char a[3] = {'a', 'b'}; // aggregate initialization
                        // (part of list initialization since C++11)
char& c = a[0];         // reference initialization

[編集] 非ローカル変数

静的記憶域期間を持つすべての非ローカル変数は、プログラムの起動の一部として、main関数の実行が開始される前に初期化されます (下記で説明する遅延の場合を除く)。スレッドローカル記憶域期間を持つすべての非ローカル変数は、スレッドの起動の一部として、スレッド関数の実行が開始される前に順序付けられて初期化されます。これらの両方のクラスの変数について、初期化は2つの異なる段階で行われます。

[編集] 静的初期化

静的初期化には2つの形式があります。

1) 可能であれば、定数初期化が適用されます。
2) そうでなければ、非ローカルの静的変数およびスレッドローカル変数はゼロ初期化されます。

実際には、

  • 定数初期化は通常、コンパイル時に適用されます。事前に計算されたオブジェクト表現は、プログラムイメージの一部として格納されます。コンパイラがそうしない場合でも、初期化が動的初期化の前に行われることを保証しなければなりません。
  • ゼロ初期化される変数は、プログラムイメージの .bss セグメントに配置されます。これはディスク上で領域を占有せず、プログラムをロードする際にOSによってゼロで埋められます。

[編集] 動的初期化

すべての静的初期化が完了した後、非ローカル変数の動的初期化が以下の状況で行われます。

1) 順序なし動的初期化 (Unordered dynamic initialization)。これは、明示的に特殊化されていない(静的/スレッドローカルな)クラステンプレートの静的データメンバおよび変数テンプレート(C++14以降)にのみ適用されます。このような静的変数の初期化は、他のすべての動的初期化に対して不定に順序付けられます(ただし、プログラムが変数の初期化前にスレッドを開始した場合、その初期化は順序付けられません)(C++17以降)。このようなスレッドローカル変数の初期化は、他のすべての動的初期化に対して順序付けられません。
2) 部分順序付き動的初期化 (Partially-ordered dynamic initialization)。これは、暗黙的または明示的にインスタンス化された特殊化ではないすべてのインライン変数に適用されます。もし部分順序付きのVが、すべての翻訳単位において順序付きまたは部分順序付きのWの前に定義されている場合、Vの初期化はWの初期化の前に順序付けられます (プログラムがスレッドを開始する場合はhappens-before)。
(C++17以降)
3) 順序付き動的初期化 (Ordered dynamic initialization)。これは、他のすべての非ローカル変数に適用されます。単一の翻訳単位内では、これらの変数の初期化は常に、ソースコードに出現する定義の正確な順序で順序付けられます。異なる翻訳単位にある静的変数の初期化は、不定に順序付けられます。異なる翻訳単位にあるスレッドローカル変数の初期化は、順序付けられません。

静的またはスレッド記憶域期間を持つ非ローカル変数の初期化が例外によって終了した場合、std::terminate が呼び出されます。

[編集] 早期動的初期化

コンパイラは、以下の両方の条件が真である場合、動的に初期化される変数を静的初期化の一部として (本質的にコンパイル時に) 初期化することが許可されています。

1) 初期化の動的バージョンが、その初期化の前に名前空間スコープの他のオブジェクトの値を変更しないこと。
2) 初期化の静的バージョンが、静的に初期化される必要がないすべての変数が動的に初期化された場合に動的初期化によって生成されるであろう値と同じ値を、初期化される変数に生成すること。

上記の規則のため、あるオブジェクト o1 の初期化が、動的初期化を必要とする可能性があり、かつ同じ翻訳単位で後から定義される名前空間スコープのオブジェクト o2 を参照する場合、使用される o2 の値が、完全に初期化された o2 の値 (コンパイラが o2 の初期化をコンパイル時に昇格させたため) なのか、単にゼロ初期化された o2 の値なのかは未規定です。

inline double fd() { return 1.0; }
 
extern double d1;
 
double d2 = d1;   // unspecified:
                  // dynamically initialized to 0.0 if d1 is dynamically initialized, or
                  // dynamically initialized to 1.0 if d1 is statically initialized, or
                  // statically initialized to 0.0 (because that would be its value
                  // if both variables were dynamically initialized)
 
double d1 = fd(); // may be initialized statically or dynamically to 1.0

[編集] 遅延動的初期化

動的初期化が、main関数の最初の文 (静的変数の場合) またはスレッドの初期関数 (スレッドローカル変数の場合) の前にhappens-beforeとなるか、あるいはそれ以降に遅延されるかは、実装定義です。

もし非インライン変数の(C++17以降)初期化がmain/スレッド関数の最初の文の後に遅延される場合、その初期化は、初期化される変数と同じ翻訳単位で定義された静的/スレッド記憶域期間を持つ任意の変数が最初にODR-useされる前に行われます。ある翻訳単位からどの変数も関数もODR-useされない場合、その翻訳単位で定義された非ローカル変数は決して初期化されない可能性があります (これはオンデマンドのダイナミックライブラリの振る舞いをモデル化しています)。しかし、翻訳単位から何かがODR-useされる限り、その初期化や破棄に副作用があるすべての非ローカル変数は、たとえプログラム中で使用されなくても初期化されます。

インライン変数の初期化が遅延される場合、その特定の変数が最初にODR-useされる前に行われます。

(C++17以降)
// ============
// == File 1 ==
 
#include "a.h"
#include "b.h"
 
B b;
A::A() { b.Use(); }
 
// ============
// == File 2 ==
 
#include "a.h"
 
A a;
 
// ============
// == File 3 ==
 
#include "a.h"
#include "b.h"
 
extern A a;
extern B b;
 
int main()
{
    a.Use();
    b.Use();
}
 
// If a is initialized before main is entered, b may still be uninitialized
// at the point where A::A() uses it (because dynamic initialization is
// indeterminately sequenced across translation units)
 
// If a is initialized at some point after the first statement of main (which odr-uses
// a function defined in File 1, forcing its dynamic initialization to run),
// then b will be initialized prior to its use in A::A

[編集] 静的ローカル変数

ローカル (つまりブロックスコープ) の静的変数およびスレッドローカル変数の初期化については、静的ブロックスコープ変数を参照してください。

外部または内部リンケージを持つ変数のブロックスコープ宣言では、初期化子は許可されません。そのような宣言は extern と共に現れなければならず、定義であってはなりません。

[編集] クラスメンバ

非静的データメンバは、メンバ初期化子リストまたはデフォルトメンバ初期化子で初期化できます。

[編集] ノート

非ローカル変数の破棄の順序は std::exit で説明されています。

[編集] 欠陥報告

以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。

DR 適用対象 公開された動作 正しい動作
CWG 270 C++98 クラステンプレートの静的データメンバの
初期化順序が未規定だった
明示的な特殊化と定義を除き、
順序なしとして規定された
CWG 441 C++98 静的記憶域期間を持つ非ローカル参照が
動的初期化の前に常に初期化されるとは限らなかった
静的初期化と見なされ、常に
動的初期化の前に初期化されるようになった
CWG 1415 C++98 ブロックスコープの extern 変数
宣言が定義になり得た
禁止された (そのような宣言では
初期化子が許可されない)
CWG 2599 C++98 初期化子内の関数引数の評価が
初期化の一部であるかどうかが不明確だった
初期化の一部であるとされた

[編集] 関連項目

English 日本語 中文(简体) 中文(繁體)