値初期化 (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 |