オブジェクトを
波括弧で囲まれた初期化子リスト
から初期化します。
構文
直接リスト初期化
|
|
T object
{
arg1, arg2, ...
};
|
T object
{.
des1
=
arg1
, .
des2
{
arg2
}
...
};
|
(C++20以降)
|
|
(1)
|
|
|
|
T
{
arg1, arg2, ...
}
|
T
{.
des1
=
arg1
, .
des2
{
arg2
}
...
}
|
(C++20以降)
|
|
(2)
|
|
|
|
new
T
{
arg1, arg2, ...
}
|
new
T
{.
des1
=
arg1
, .
des2
{
arg2
}
...
}
|
(C++20以降)
|
|
(3)
|
|
|
|
Class
{
T member
{
arg1, arg2, ...
}; };
|
Class
{
T member
{.
des1
=
arg1
, .
des2
{
arg2
}
...
}; };
|
(C++20以降)
|
|
(4)
|
|
|
|
Class
::
Class
() :
member
{
arg1, arg2, ...
} {...
|
Class
::
Class
() :
member
{.
des1
=
arg1
, .
des2
{
arg2
}
...
} {...
|
(C++20以降)
|
|
(5)
|
|
|
|
コピーリスト初期化
|
|
T オブジェクト
= {
arg1, arg2, ...
};
|
T オブジェクト
= {.
des1
=
arg1
, .
des2
{
arg2
}
...
};
|
(C++20以降)
|
|
(6)
|
|
|
|
関数
({
arg1, arg2, ...
})
|
関数
({.
des1
=
arg1
, .
des2
{
arg2
}
...
})
|
(C++20以降)
|
|
(7)
|
|
|
|
return {
arg1, arg2, ...
};
|
return
{.
des1
=
arg1
, .
des2
{
arg2
}
...
};
|
(C++20以降)
|
|
(8)
|
|
|
|
オブジェクト
[{
arg1, arg2, ...
}]
|
オブジェクト
[{.
des1
=
arg1
, .
des2
{
arg2
}
...
}]
|
(C++20以降)
|
|
(9)
|
|
|
|
オブジェクト
= {
arg1, arg2, ...
}
|
オブジェクト
= {.
des1
=
arg1
, .
des2
{
arg2
}
...
}
|
(C++20以降)
|
|
(10)
|
|
|
|
U
({
arg1, arg2, ...
})
|
U
({.
des1
=
arg1
, .
des2
{
arg2
}
...
})
|
(C++20以降)
|
|
(11)
|
|
|
|
クラス
{
T メンバー
= {
arg1, arg2, ...
}; };
|
クラス
{
T メンバー
= {.
des1
=
arg1
, .
des2
{
arg2
}
...
}; };
|
(C++20以降)
|
|
(12)
|
|
|
|
リスト初期化は以下の状況で実行されます:
-
direct-list-initialization (明示的および非明示的コンストラクタの両方が考慮されます)
1)
中括弧で囲まれた初期化リストを使用した名前付き変数の初期化
2)
中括弧で囲まれた初期化リストによる無名一時オブジェクトの初期化
-
copy-list-initialization (明示的および非明示的コンストラクタの両方が考慮されますが、呼び出し可能なのは非明示的コンストラクタのみです)
6)
等号の後に波括弧で囲まれた初期化子リストを使用した名前付き変数の初期化
7)
関数呼び出し式において、引数として波括弧で囲まれた初期化子リストが使用され、リスト初期化によって関数パラメータが初期化される場合
8)
リターン文で、戻り値式として中括弧で囲まれた初期化子リストが使用され、リスト初期化によって返されるオブジェクトが初期化される場合
9)
ユーザー定義の
添字演算子
を持つ
operator[]
の
添字式
において、リスト初期化がオーバーロードされた演算子のパラメータを初期化する場合
10)
代入式において、リスト初期化がオーバーロードされた演算子のパラメータを初期化する場合
11)
関数形式キャスト式
またはその他のコンストラクタ呼び出し。波括弧で囲まれた初期化子リストがコンストラクタ引数の代わりに使用される場合。コピーリスト初期化はコンストラクタのパラメータを初期化する(注記:この例における型
U
はリスト初期化されている型ではない;
U
のコンストラクタのパラメータが対象である)
説明
(場合によっては cv 修飾された)型
T
のオブジェクトに対するリスト初期化の効果は以下の通りです:
-
波括弧で囲まれた初期化子リストが
指示付き初期化子リスト
を含み、かつ
T
が参照型でない場合、
T
は集成体クラスでなければならない。指示付き初期化子リストの指示子における順序付けられた識別子は、
T
の直接の非静的データメンバーの順序付けられた識別子の部分列を形成しなければならない。
集成体初期化
が実行される。
|
(C++20以降)
|
-
それ以外の場合、
T
がクラス型である場合、
T
のコンストラクタが2つのフェーズで考慮されます:
-
-
-
前の段階で一致が生成されない場合、
T
のすべてのコンストラクタが、
波括弧初期化子リストの初期化節で構成される引数セットに対して
オーバーロード解決
に参加します(ただし、非縮小変換のみが許可されます)。この段階でコピーリスト初期化の最適一致として明示的コンストラクタが生成された場合、コンパイルは失敗します(単純なコピー初期化では、明示的コンストラクタは一切考慮されないことに注意してください)。
-
それ以外の場合、
T
が固定された基底型
U
を持つ
列挙型
であり、波括弧で囲まれた初期化子リストが単一の初期化子
v
のみを持ち、以下の条件がすべて満たされる場合、列挙型は
v
を
U
に変換した結果で初期化される:
-
初期化が直接リスト初期化である。
-
v
が
スカラ型
である。
-
v
が
U
に暗黙的に変換可能である。
-
v
から
U
への変換が縮小変換ではない。
|
(C++17以降)
|
-
それ以外の場合(
T
がクラス型でない場合)、波括弧で囲まれた初期化子リストが単一の初期化節のみを持ち、かつ
T
が参照型でないか、または参照型でありその参照先の型が初期化節の型と同じかその基底クラスである場合、
T
は
direct-initialized
(直接リスト初期化の場合)または
copy-initialized
(コピーリスト初期化の場合)されます。ただし、縮小変換は許可されません。
-
それ以外の場合、
T
が初期化子句の型と互換性のない参照型である場合:
-
-
T
によって参照される型のprvalue一時オブジェクトがコピーリスト初期化され、参照がその一時オブジェクトに束縛される(参照が非const左辺値参照の場合、これは失敗する)。
|
(C++17まで)
|
-
-
prvalueが生成される。prvalueはコピーリスト初期化によってその結果オブジェクトを初期化する。その後、prvalueは参照を直接初期化するために使用される(参照が非const左辺値参照の場合、これは失敗する)。一時オブジェクトの型は
T
によって参照される型である
、ただし
T
が「
U
の未知のサイズの配列への参照」である場合を除き、その場合の一時オブジェクトの型は宣言
U x[] H
における
x
の型である(
H
は初期化子リスト)
(C++20以降)
。
|
(C++17以降)
|
std::
initializer_list
<
E
>
型のオブジェクトは、初期化子リストから、あたかもコンパイラが
以下のものを生成し
実体化
(C++17以降)
したかのように構築されます:
prvalue
型「
N
個の
const
E
の配列」、ここで
N
は初期化子リスト内の初期化子節の数です。これは初期化子リストの
バッキング配列
と呼ばれます。
バッキング配列の各要素は、初期化リストの対応する初期化節で
copy-initialized
され、
std::
initializer_list
<
E
>
オブジェクトはその配列を参照するように構築されます。コピーに対して選択されたコンストラクタまたは変換関数は、初期化リストのコンテキストで
accessible
である必要があります。いずれかの要素を初期化するために縮小変換が必要な場合、プログラムは不適格となります。
バッキング配列は他のあらゆる
一時オブジェクト
と同じ生存期間を持ちますが、
std::initializer_list
オブジェクトをバッキング配列から初期化する場合、配列の生存期間は
一時オブジェクトへの参照のバインド
と同様に延長されます。
void f(std::initializer_list<double> il);
void g(float x)
{
f({1, x, 3});
}
void h()
{
f({1, 2, 3});
}
struct A { mutable int i; };
void q(std::initializer_list<A>);
void r()
{
q({A{1}, A{2}, A{3}});
}
// 上記の初期化は、以下とほぼ同等の方法で実装されます。
// コンパイラがinitializer_listオブジェクトを一対のポインタで構築可能であると仮定し、
// `__b`が`f`の呼び出しよりも長く生存しないことを理解した上で。
void g(float x)
{
const double __a[3] = {double{1}, double{x}, double{3}}; // バッキング配列
f(std::initializer_list<double>(__a, __a + 3));
}
void h()
{
static constexpr double __b[3] =
{double{1}, double{2}, double{3}}; // バッキング配列
f(std::initializer_list<double>(__b, __b + 3));
}
void r()
{
const A __c[3] = {A{1}, A{2}, A{3}}; // バッキング配列
q(std::initializer_list<A>(__c, __c + 3));
}
すべてのバッキング配列が互いに区別されるか(つまり、
非オーバーラップオブジェクト
に格納されているか)は未規定です:
bool fun(std::initializer_list<int> il1, std::initializer_list<int> il2)
{
return il2.begin() == il1.begin() + 1;
}
bool overlapping = fun({1, 2, 3}, {2, 3, 4}); // 結果は未規定:
// 後方の配列は {1, 2, 3, 4} 内で
// ストレージを共有する可能性がある
縮小変換
リスト初期化は、以下を禁止することで許可される
暗黙変換
を制限します:
-
浮動小数点型
T
からの変換で、変換先の浮動小数点型の
浮動小数点変換ランク
が
T
のランク以下であり、かつ変換結果が
定数式
でない場合を除く。ただし以下の条件のいずれかを満たす場合は例外:
-
変換後の値が有限であり、かつ変換がオーバーフローしない場合
-
変換前後の値がどちらも有限でない場合
-
整数型から浮動小数点型への変換。ただし、ソースが定数式であり、その値がターゲット型に正確に格納できる場合は除く
-
整数型またはスコープなし列挙型から、元の値をすべて表現できない整数型への変換。ただし以下の場合は除く:
-
変換元が
ビットフィールド
であり、その幅
w
がその型(または
列挙型
の場合その基底型)の幅より小さく、かつ変換先の型が元の型と同じ符号性を持ち幅
w
の仮想的な拡張整数型のすべての値を表現できる場合、または
-
変換元が定数式であり、その値が変換先の型に正確に格納できる場合
-
ポインタ型またはポインタ-to-member型から
bool
への変換
注記
すべての初期化句は、
sequenced before
中括弧で囲まれた初期化リスト内でそれに続く初期化句よりも前に評価されます。これは
function call expression
の引数とは対照的であり、引数は
unsequenced
(until C++17)
indeterminately sequenced
(since C++17)
です。
HTMLタグ、属性、C++専門用語("sequenced before"、"function call expression"、"unsequenced"、"indeterminately sequenced")は翻訳せず、元のフォーマットを保持しています。
波括弧で囲まれた初期化子リストは式ではなく、したがって型を持ちません。例えば、
decltype
(
{
1
,
2
}
)
は不適格です。型を持たないということは、テンプレートの型推論が波括弧で囲まれた初期化子リストに一致する型を推論できないことを意味します。したがって、宣言
template
<
class
T
>
void
f
(
T
)
;
が与えられた場合、式
f
(
{
1
,
2
,
3
}
)
は不適格です。ただし、テンプレートパラメータは他の方法で推論可能です。例えば、
std::
vector
<
int
>
v
(
std::
istream_iterator
<
int
>
(
std::
cin
)
,
{
}
)
の場合、イテレータ型は最初の引数で推論されますが、2番目のパラメータ位置でも使用されます。特別な例外として、
キーワード
auto
を使用した型推論
では、コピーリスト初期化において波括弧で囲まれた初期化子リストを
std::initializer_list
として推論します。
また、波括弧で囲まれた初期化子リストには型がないため、
オーバーロード解決の特別な規則
が、オーバーロードされた関数呼び出しの引数として使用される場合に適用されます。
集成型は同じ型の単一初期化節を持つ波括弧で囲まれた初期化子リストから直接コピー/ムーブ初期化しますが、非集成型はまず
std::initializer_list
コンストラクタを考慮します:
struct X {}; // 集成体
struct Q // 非集成体
{
Q() = default;
Q(Q const&) = default;
Q(std::initializer_list<Q>) {}
};
int main()
{
X x;
X x2 = X{x}; // コピーコンストラクタ(集成体初期化ではない)
Q q;
Q q2 = Q{q}; // 初期化子リストコンストラクタ(コピーコンストラクタではない)
}
一部のコンパイラ(例:gcc 10)は、C++20モードではポインタまたはポインタ-to-memberから
bool
への変換のみを縮小変換と見なします。
例
#include <iostream>
#include <map>
#include <string>
#include <vector>
struct Foo
{
std::vector<int> mem = {1, 2, 3}; // 非静的メンバーのリスト初期化
std::vector<int> mem2;
Foo() : mem2{-1, -2, -3} {} // コンストラクタ内でのメンバーのリスト初期化
};
std::pair<std::string, std::string> f(std::pair<std::string, std::string> p)
{
return {p.second, p.first}; // return文でのリスト初期化
}
int main()
{
int n0{}; // 値初期化(ゼロに)
int n1{1}; // 直接リスト初期化
std::string s1{'a', 'b', 'c', 'd'}; // 初期化子リストコンストラクタ呼び出し
std::string s2{s1, 2, 2}; // 通常のコンストラクタ呼び出し
std::string s3{0x61, 'a'}; // 初期化子リストコンストラクタが(int, char)より優先される
int n2 = {1}; // コピーリスト初期化
double d = double{1.2}; // 右辺値のリスト初期化、その後コピー初期化
auto s4 = std::string{"HelloWorld"}; // 同上、C++17以降は一時オブジェクトは生成されない
// created since C++17
std::map<int, std::string> m = // ネストされたリスト初期化
{
{1, "a"},
{2, {'a', 'b', 'c'}},
{3, s1}
};
std::cout << f({"hello", "world"}).first // 関数呼び出しでのリスト初期化
<< '\n';
const int (&ar)[2] = {1, 2}; // 一時配列への左辺値参照のバインド
int&& r1 = {1}; // 一時intへの右辺値参照のバインド
// int& r2 = {2}; // エラー: 右辺値を非const左辺値参照にバインドできない
// int bad{1.0}; // エラー: 縮小変換
unsigned char uc1{10}; // OK
// unsigned char uc2{-1}; // エラー: 縮小変換
Foo f;
std::cout << n0 << ' ' << n1 << ' ' << n2 << '\n'
<< s1 << ' ' << s2 << ' ' << s3 << '\n';
for (auto p : m)
std::cout << p.first << ' ' << p.second << '\n';
for (auto n : f.mem)
std::cout << n << ' ';
for (auto n : f.mem2)
std::cout << n << ' ';
std::cout << '\n';
[](...){}(d, ar, r1, uc1); // [[maybe_unused]]と同等の効果
}
出力:
world
0 1 1
abcd cd aa
1 a
2 abc
3 abcd
1 2 3 -1 -2 -3
不具合報告
以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。
|
DR
|
適用対象
|
公開時の動作
|
正しい動作
|
|
CWG 1288
|
C++11
|
単一初期化節を持つ波括弧で囲まれた初期化リストによる参照のリスト初期化は常に一時オブジェクトへの参照束縛を行っていた
|
有効な場合はその初期化節に束縛する
|
|
CWG 1290
|
C++11
|
バッキング配列の寿命が正しく規定されていなかった
|
他の一時オブジェクトと同じように規定
|
|
CWG 1324
|
C++11
|
{}
からの初期化で初期化が最初に考慮されていた
|
集成体初期化が最初に考慮される
|
|
CWG 1418
|
C++11
|
バッキング配列の型に
const
が欠けていた
|
const
が追加された
|
|
CWG 1467
|
C++11
|
集成体と文字配列の同一型初期化が禁止されていた
単一節リストでは初期化リストコンストラクタがコピーコンストラクタより優先された
|
同一型初期化が許可される
単一節リストは直接初期化する
|
|
CWG 1494
|
C++11
|
互換性のない型の初期化節による参照のリスト初期化時、
作成される一時オブジェクトが直接リスト初期化かコピーリスト初期化かが規定されていなかった
|
参照の初期化の種類に依存する
|
|
CWG 2137
|
C++11
|
{X}
からの
X
のリスト初期化時に初期化リストコンストラクタがコピーコンストラクタに負けていた
|
非集成体は初期化リストを最初に考慮する
|
|
CWG 2252
|
C++17
|
列挙型が非スカラー値からリスト初期化可能だった
|
禁止
|
|
CWG 2267
|
C++11
|
CWG issue 1494
の解決により一時オブジェクトが直接リスト初期化可能であることが明確になった
|
参照をリスト初期化する際はコピーリスト初期化される
|
|
CWG 2374
|
C++17
|
列挙型の直接リスト初期化で過剰なソース型が許可されていた
|
制限された
|
|
CWG 2627
|
C++11
|
より大きな整数型の狭いビットフィールドはより小さな整数型に昇格可能だが、それでも縮小変換と見なされていた
|
縮小変換ではない
|
|
CWG 2713
|
C++20
|
集成体クラスへの参照は指示付き初期化リストで初期化できなかった
|
許可される
|
|
CWG 2830
|
C++11
|
リスト初期化がトップレベルのCV修飾を無視していなかった
|
無視する
|
|
CWG 2864
|
C++11
|
オーバーフローする浮動小数点変換は縮小変換ではなかった
|
縮小変換である
|
|
P1957R2
|
C++11
|
ポインタ/ポインタ-to-メンバから
bool
への変換は縮小変換ではなかった
|
縮小変換と見なされる
|
|
P2752R3
|
C++11
|
寿命が重複するバッキング配列は重複できなかった
|
重複可能
|
関連項目