ホームに戻る
出典 :
一様初期化 - cpprefjp C++日本語リファレンス リスト初期化と一様初期化|C++11実践復習(β) C++でクリーンなコードの書き方 - Qiita
関連 :
C++11 コンストラクタ初期化子 デフォルトメンバ初期化子
目次 :

一様初期化 (uniform initialization)

コンストラクタ呼び出しを、 () (丸括弧)ではなく {} (波括弧)で行う記法のこと。
これにより、Cスタイルの構造体(集成体)、配列、クラスなどを共通の構文で初期化することができる。
C++11で追加された機能で、C形式の初期化リスト構文の拡張である。「統一初期化記法」とも呼ばれる。
C++03
// 配列 : 初期化リストによる初期化 int a[] = {1, 2, 3}; // ベクトル : 初期化リストが使用できない vector<int> v; v.reserve(3); v.push_back(1); v.push_back(2); v.push_back(3); // 構造体(またはクラス) : 初期化とコンストラクタ呼び出しで構文が異なる struct T1 { int a; double b; }; struct T2 { T2(int, double); }; T1 t1_1 = {1, 1.0}; //< 集成体初期化 T1 t1_2{1, 1.0}; //< 集成体初期化 T2 t2_1(1, 1.0); //< コンストラクタ呼び出し
C++11 : 共通の構文で初期化
// 配列 int a[] = { 1, 2, 3 } ; // ベクトル vector<int> v = { 1, 2, 3 } ; // 構造体(またはクラス) struct T1 { int a; double b; }; struct T2 { T2(int, double); }; // 集成体初期化(いずれの記法も等価) T1 t1_1 = { 1, 1.0 } ; T1 t1_2 { 1, 1.0 } ; // コンストラクタ呼び出し(いずれの記法も等価) T2 t2_1 = { 1, 1.0 } ; T2 t2_2 { 1, 1.0 } ;
集成体初期化とコンストラクタ呼び出しで記法に差異が無い。
コンストラクタが定義されている場合のみコンストラクタが呼ばれる。

return 文でオブジェクトを返却

一様初期化により、戻り値の型が確定している文脈では、コンストラクタ引数を {} で列挙したものを return 文で返すことができる。
これは関数の引数として用いる場合も同様である。
#include <string> struct X { X(int, double, std::string) {} }; X createX() { // 戻り値の型は X であることが明白 // ⇒ 型推論 return { 1, 3.14, "hello" } ; }
上記の createX() 関数は以下と等価である。
X createX() { // X 型の一時変数を一様初期化 X temp = { 1, 3.14, "hello" } ; // 一時変数を返す return temp; }

初期化の意図を明示

() による初期化は、状況によっては関数宣言と解釈されることがあるが、{} を用いることで変数の初期化であることが明確となる。
(丸括弧を用いたコンストラクタ呼び出しはプロトタイプ宣言との文法の違いが曖昧で、コンパイラが区別できないことがある。)
struct X {}; int main() { X x(); // x : 引数なし、戻り値 X 型の関数宣言 ⇒ 意図と異なる X y{}; // y : X 型の変数(デフォルトコンストラクタで初期化) }

引数の評価順を保証

() で引数を渡した場合、引数同士の評価順は一定ではないが、{} を用いた場合は必ず左から順に評価される

注意が必要な点

初期化子リスト( std::initializer_list )を引数に取るコンストラクタと、
初期化子リストの要素と同じ型の引数を受け取るコンストラクタが共に存在する場合、
一様初期化を用いると std::initializer_list を受け取るコンストラクタが優先して呼ばれる。
また、 std::initializer_list を引数に取るコンストラクタと、デフォルトコンストラクタが存在する場合、
空の {} が渡された際はデフォルトコンストラクタが呼ばれる。
std::vector<int> v1 ( 4, 10 ) ; //< 4 つの 10 でベクトルを初期化 ⇒ { 10, 10, 10, 10 } //< (初期化子リストと同じ型の引数をとるコンストラクタが呼ばれる) std::vector<int> v2 { 4, 10 } ; //< { 4, 10 } の2要素でベクトルを初期化 ⇒ { 4, 10 } //< (初期化子リストを引数にとるコンストラクタが呼ばれる) //< ( v2 ( { 4, 10 } ) と等価) std::vector<int> v3 { } ; //< デフォルトコンストラクタが呼ばれる //< (空の初期化子リストとは解釈されない)
上記の例において、v1 と v2 は共に 4 と 10 という値をコンストラクタに渡しているが、
v1 ではコンストラクタ引数(パラメータリスト)、v2 では初期化子リストと解釈されるため、初期化の結果は異なる。
(一様初期化を用いた v2 は std::initializer_list を引数に取るコンストラクタが呼ばれる。)
std::initializer_list でない側のコンストラクタを呼びたい場合は、{} ではなく () を用いる必要がある。

また、v3 は「空の std::initializer_list 」とは解釈されず、デフォルトコンストラクタが呼ばれる。