値初期化 (C++03以上)
変数が空の初期化子で構築されたときに行われる初期化です。
目次 |
[編集] 構文
T()
|
(1) | ||||||||
new T ()
|
(2) | ||||||||
Class:: Class( ...) : member() { ... }
|
(3) | ||||||||
T object {};
|
(4) | (C++11以上) | |||||||
T{}
|
(5) | (C++11以上) | |||||||
new T {}
|
(6) | (C++11以上) | |||||||
Class:: Class( ...) : member{} { ... }
|
(7) | (C++11以上) | |||||||
[編集] 説明
値初期化は以下の状況で行われます。
4) 名前付きの変数 (自動、静的、またはスレッドローカル) が空の波括弧から構成される初期化子を用いて宣言されたとき。
|
(C++11以上) |
すべての場合において、空の波括弧 {}
が使用され、 T
が集成体型の場合は、値初期化の代わりに集成体初期化が行われます。
T
がデフォルトコンストラクタを持たないけれども std::initializer_list を取るコンストラクタを持つクラス型の場合は、リスト初期化が行われます。
値初期化の効果は以下の通りです。
2) T がいかなるユーザ提供のコンストラクタも持たない非 union クラス型の場合は、 T のすべての非静的データメンバおよび基底クラスのコンポーネントが値初期化されます。
|
(C++11未満) |
(C++11以上) |
T
が配列型の場合は、配列の各要素が値初期化されます。[編集] ノート
コンストラクタがユーザによって宣言され、その最初の宣言が明示的にデフォルト化されていない場合、そのコンストラクタはユーザ提供されていると言います。
構文 T object(); は、オブジェクトを初期化するのではなく、引数を取らず T
を返す関数を宣言します。 C++11 より前で名前付き変数を値初期化する方法は T object = T(); です。 これは一時オブジェクトを値初期化し、それからオブジェクトをコピー初期化します。 この場合ほとんどのコンパイラは最適化によってコピーを省略します。
C++03 (値初期化が導入された) より前の C++98 では、式 new T() はデフォルト初期化として分類され、ゼロ初期化が規定されていました。
参照は値初期化できません。
関数キャストで説明されている通り、配列に対しては構文 T() (1) は禁止されていますが、 T{} (5) は許されています。
すべての標準のコンテナ (std::vector や std::list など) は、単一の size_type
引数を用いて構築されたとき、または resize() の呼び出しによって拡張されたとき、その要素を値初期化します。
C++11 以降、ユーザ提供コンストラクタを持つクラス型のメンバを持ち、ユーザ提供コンストラクタを持たないクラスの値初期化は、そのコンストラクタを呼ぶ前にそのメンバをゼロクリアします。
#include <iostream> struct A { int i; A() { } // ユーザ提供デフォルトコンストラクタ (i を初期化しない) }; struct B { A a; }; // 暗黙に定義されたデフォルトコンストラクタ int main() { std::cout << B().a.i << '\n'; // 一時オブジェクト B を値初期化します。 // C++03 では b.a.i は未初期化になります。 // C++11 では b.a.i はゼロに設定されます。 // C++11 では B{}.a.i は b.a.i を未初期化にしますが、理由は異なります。 // DR1301 以降の C++11 では、 B{} は集成体初期化であり、 // それはユーザ提供コンストラクタを持つ A を値初期化します。 }
[編集] 例
#include <string> #include <vector> #include <iostream> struct T1 { int mem1; std::string mem2; }; // 暗黙のデフォルトコンストラクタ。 struct T2 { int mem1; std::string mem2; T2(const T2&) { } // ユーザ定義コピーコンストラクタ。 }; // デフォルトコンストラクタはありません。 struct T3 { int mem1; std::string mem2; T3() { } // ユーザ定義デフォルトコンストラクタ。 }; std::string s{}; // クラス → デフォルト初期化、値は ""。 int main() { int n{}; // スカラー → ゼロ初期化、値は 0。 double f = double(); // スカラー → ゼロ初期化、値は 0.0。 int* a = new int[10](); // 配列 → 各要素の値初期化、各要素の値は 0。 T1 t1{}; // 暗黙のデフォルトコンストラクタを持つクラス → // t1.mem1 はゼロ初期化、値は 0。 // t1.mem2 はデフォルト初期化、値は ""。 // T2 t2{}; // デフォルトコンストラクタを持たないクラス → エラー。 T3 t3{}; // ユーザ定義デフォルトコンストラクタを持つクラス → // t3.mem1 はデフォルト初期化、値は不定。 // t3.mem2 はデフォルト初期化、値は ""。 std::vector<int> v(3); // 各要素の値初期化、各要素の値は 0。 std::cout << s.size() << ' ' << n << ' ' << f << ' ' << a[9] << ' ' << v[2] << '\n'; std::cout << t1.mem1 << ' ' << t3.mem1 << '\n'; delete[] a; }
出力例:
0 0 0 0 0 0 4199376
[編集] 欠陥報告
以下の動作変更欠陥報告は以前に発行された C++ 標準に遡って適用されました。
DR | 適用先 | 発行時の動作 | 正しい動作 |
---|---|---|---|
CWG 178 | C++98 | there's no value-initialization; empty initializer invoke default-init (though new T() also performs zero-init)
|
empty initializer invoke value-init |
CWG 1301 | C++11 | defaulted default constructor skipped zero-init before construction | zero-init performed |