値初期化 (C++03以上)
変数が空の初期化子で構築されたときに行われる初期化です。
目次 |
[編集]構文
T() | (1) | ||||||||
new T() | (2) | ||||||||
Class:: Class( ...) : member() { ...} | (3) | ||||||||
Tobject{}; | (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 |