名前空間
変種
操作

リスト初期化 (C++11 以降)

From cppreference.com
< cpp‎ | language
 
 
C++言語
全般
フロー制御
条件実行文
if
繰り返し文 (ループ)
for
範囲for (C++11)
ジャンプ文
関数
関数宣言
ラムダ式
inline指定子
動的例外仕様 (C++17まで*)
noexcept指定子 (C++11)
例外
名前空間
指定子
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
記憶域期間指定子
初期化
集成体初期化
リスト初期化 (C++11)  
定数初期化
参照初期化

代替表現
リテラル
ブーリアン - 整数 - 浮動小数点数
文字 - 文字列 - nullptr (C++11)
ユーザー定義 (C++11)
ユーティリティ
属性 (C++11)
typedef宣言
型エイリアス宣言 (C++11)
キャスト
メモリ確保
クラス
クラス固有の関数プロパティ
explicit (C++11)
static

特殊メンバ関数
テンプレート
その他
 
 

波括弧で囲まれた初期化子リストからオブジェクトを初期化します。

目次

[編集] 構文

[編集] 直接リスト初期化

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 object = { arg1, arg2, ... };

T object = {.des1 = arg1 , .des2 { arg2 } ... };

(C++20以降)
(6)
function ({ arg1, arg2, ... })

function ({.des1 = arg1 , .des2 { arg2 } ... })

(C++20以降)
(7)
return { arg1, arg2, ... };

return {.des1 = arg1 , .des2 { arg2 } ... };

(C++20以降)
(8)
object [{ arg1, arg2, ... }]

object [{.des1 = arg1 , .des2 { arg2 } ... }]

(C++20以降)
(9)
object = { arg1, arg2, ... }

object = {.des1 = arg1 , .des2 { arg2 } ... }

(C++20以降)
(10)
U ({ arg1, arg2, ... })

U ({.des1 = arg1 , .des2 { arg2 } ... })

(C++20以降)
(11)
Class { T member = { arg1, arg2, ... }; };

Class { T member = {.des1 = arg1 , .des2 { arg2 } ... }; };

(C++20以降)
(12)

リスト初期化は以下の状況で実行されます。

  • 直接リスト初期化 (明示的および非明示的コンストラクタの両方が考慮されます)
1) 波括弧で囲まれた初期化子リストによる名前付き変数の初期化
2) 波括弧で囲まれた初期化子リストによる無名の一時オブジェクトの初期化
3) 初期化子が波括弧で囲まれた初期化子リストであるnew式による動的ストレージ期間を持つオブジェクトの初期化
4) 等号を使用しない非静的データメンバ初期化子
5) 波括弧で囲まれた初期化子リストが使用される場合のコンストラクタのメンバ初期化子リスト
  • コピーリスト初期化 (明示的および非明示的コンストラクタの両方が考慮されますが、非明示的コンストラクタのみが呼び出される場合があります)
6) 等号の後の波括弧で囲まれた初期化子リストによる名前付き変数の初期化
7) 関数呼び出し式で、引数として波括弧で囲まれた初期化子リストが使用され、リスト初期化が関数パラメータを初期化する場合
8) return 文で、戻り値式として波括弧で囲まれた初期化子リストが使用され、リスト初期化が返されるオブジェクトを初期化する場合
9) ユーザー定義のoperator[]を持つ添字式で、リスト初期化がオーバーロードされた演算子のパラメータを初期化する場合
10) 代入式で、リスト初期化がオーバーロードされた演算子のパラメータを初期化する場合
11) 関数形式のキャスト式またはその他のコンストラクタ呼び出しで、コンストラクタ引数の代わりに波括弧で囲まれた初期化子リストが使用される場合。コピーリスト初期化がコンストラクタのパラメータを初期化します (注: この例の型Uはリスト初期化される型ではありません。Uのコンストラクタのパラメータがリスト初期化されます)。
12) 等号を使用する非静的データメンバ初期化子

[編集] 解説

型 (おそらくcv修飾子付き) Tのオブジェクトのリスト初期化の効果は以下の通りです。

  • 波括弧で囲まれた初期化子リストに指定初期化子リストが含まれており、Tが参照型でない場合、Tは集約クラスでなければなりません。指定初期化子リストの指定子の順序付けられた識別子は、Tの直接非静的データメンバの順序付けられた識別子の部分列を形成しなければなりません。集約初期化が実行されます。
(C++20以降)
  • それ以外の場合、Tが集約クラスであり、波括弧で囲まれた初期化子リストに指定初期化子リストが含まれておらず、(C++20 以降)単一の初期化句が同じ型または派生型(おそらくcv修飾子付き)である場合、オブジェクトはその初期化句から初期化されます(コピーリスト初期化の場合はコピー初期化によって、直接リスト初期化の場合は直接初期化によって)。
  • それ以外の場合、Tが文字配列であり、波括弧で囲まれた初期化子リストが適切に型付けされた文字列リテラルである単一の初期化句を持つ場合、配列は通常通り文字列リテラルから初期化されます。
  • それ以外の場合、波括弧で囲まれた初期化子リストが空であり、Tがデフォルトコンストラクタを持つクラス型の場合、値初期化が実行されます。
  • それ以外の場合、Tstd::initializer_listの特殊化である場合、オブジェクトは下記のように初期化されます。
  • それ以外の場合、Tがクラス型である場合、Tのコンストラクタは2つのフェーズで考慮されます。
  • std::initializer_listを唯一の引数として取るコンストラクタ、または残りの引数がデフォルト値を持つ場合に最初の引数として取るすべてのコンストラクタが検査され、std::initializer_list型の単一の引数に対してオーバーロード解決によってマッチングされます。
  • 前の段階でマッチが見つからない場合、Tのすべてのコンストラクタは、波括弧で囲まれた初期化子リストの初期化句からなる引数セットに対してオーバーロード解決に参加します。ただし、非縮小変換のみが許可されます。この段階でコピーリスト初期化の最良マッチとして明示的コンストラクタが生成された場合、コンパイルは失敗します(単純なコピー初期化では、明示的コンストラクタはまったく考慮されないことに注意してください)。
  • それ以外の場合、Tが固定の基底型Uを持つ列挙型であり、波括弧で囲まれた初期化子リストが単一の初期化子vを持ち、以下のすべての条件が満たされる場合、列挙型はvUに変換した結果で初期化されます。
    • 初期化は直接リスト初期化です。
    • vスカラ型です。
    • vUに暗黙的に変換可能です。
    • vからUへの変換は縮小変換ではありません。
(C++17以降)
  • それ以外の場合(Tがクラス型でない場合)、波括弧で囲まれた初期化子リストが単一の初期化句を持ち、Tが参照型でないか、またはその参照型が初期化句の型と同じか基底クラスである参照型である場合、Tは(直接リスト初期化では)直接初期化されるか、(コピーリスト初期化では)コピー初期化されますが、縮小変換は許可されません。
  • それ以外の場合、Tが初期化句の型と互換性のない参照型である場合
  • Tによって参照される型のprvalue一時オブジェクトがコピーリスト初期化され、参照はその一時オブジェクトに束縛されます(参照が非const左辺値参照である場合、これは失敗します)。
(C++17まで)
  • prvalueが生成されます。prvalueはコピーリスト初期化によって結果オブジェクトを初期化します。その後、prvalueは参照を直接初期化するために使用されます(参照が非const左辺値参照である場合、これは失敗します)。一時オブジェクトの型はTによって参照される型です。ただし、Tが「不明な境界のUの配列への参照」である場合、一時オブジェクトの型は宣言U x[] Hにおけるxの型であり、ここでHは初期化子リストです。(C++20 以降)
(C++17以降)
  • それ以外の場合、波括弧で囲まれた初期化子リストに初期化句がない場合、T値初期化されます。

[編集] std::initializer_list のリスト初期化

std::initializer_list<E>型のオブジェクトは、コンパイラが「N個のconst Eの配列」のprvalueを生成し実体化したかのように(C++17 以降)初期化子リストから構築されます。ここでNは初期化子リスト内の初期化句の数です。これは初期化子リストの*バッキング配列*と呼ばれます。

バッキング配列の各要素は、初期化子リストの対応する初期化句でコピー初期化され、std::initializer_list<E>オブジェクトはその配列を参照するように構築されます。コピーのために選択されたコンストラクタまたは変換関数は、初期化子リストのコンテキストでアクセス可能である必要があります。いずれかの要素を初期化するために縮小変換が必要な場合、プログラムは不正な形式となります。

バッキング配列は他の一時オブジェクトと同じ寿命を持ちますが、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}});
}
 
// The initialization above will be implemented in a way roughly equivalent to below,
// assuming that the compiler can construct an initializer_list object with a pair of
// pointers, and with the understanding that `__b` does not outlive the call to `f`.
 
void g(float x)
{
    const double __a[3] = {double{1}, double{x}, double{3}}; // backing array
    f(std::initializer_list<double>(__a, __a + 3));
}
 
void h()
{
    static constexpr double __b[3] =
        {double{1}, double{2}, double{3}}; // backing array
    f(std::initializer_list<double>(__b, __b + 3));
}
 
void r()
{
    const A __c[3] = {A{1}, A{2}, A{3}}; // backing array
    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}); // the result is unspecified:
                                              // the back arrays can share
                                              // storage within {1, 2, 3, 4}

[編集] 縮小変換

リスト初期化では、以下の暗黙の変換を禁止することで、許容される変換を制限します。

  • 浮動小数点型から整数型への変換
  • 浮動小数点型Tから、浮動小数点変換ランクTのそれよりも大きくも等しくもない別の浮動小数点型への変換。ただし、変換結果が定数式であり、以下のいずれかの条件が満たされる場合を除く。
    • 変換された値が有限であり、変換によってオーバーフローが発生しない。
    • 変換前後の値がともに有限でない。
  • 整数型から浮動小数点型への変換。ただし、ソースが定数式であり、その値がターゲット型に正確に格納できる場合を除く。
  • 元のすべての値を表現できない整数型またはスコープなし列挙型から整数型への変換。ただし、以下のいずれかの場合を除く。
    • ソースが幅wがその型(または列挙型の場合、その基底型)の幅よりも小さいビットフィールドであり、ターゲット型が幅wで元の型と同じ符号を持つ仮説的な拡張整数型のすべての値を表現できる場合、または
    • ソースが定数式であり、その値がターゲット型に正確に格納できる場合
  • ポインタ型またはポインタ・トゥ・メンバ型からboolへの変換

[編集] 備考

各初期化句は、波括弧で囲まれた初期化子リスト内でそれに続くどの初期化句よりも先行して実行されます。これは、関数呼び出し式の引数が順序付けされない(C++17まで)不定順序付け(C++17以降)であるのとは対照的です。

波括弧で囲まれた初期化子リストは式ではないため、型を持ちません。例えば、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 {}; // aggregate
 
struct Q     // non-aggregate
{
    Q() = default;
    Q(Q const&) = default;
    Q(std::initializer_list<Q>) {}
};
 
int main()
{
    X x;
    X x2 = X{x}; // copy-constructor (not aggregate initialization)
 
    Q q;
    Q q2 = Q{q}; // initializer-list constructor (not copy constructor)
}

一部のコンパイラ(例:gcc 10)は、C++20モードでのみポインタまたはポインタ・トゥ・メンバからboolへの変換を縮小変換とみなします。

機能テストマクロ 規格 機能
__cpp_initializer_lists 200806L (C++11) リスト初期化とstd::initializer_list

[編集]

#include <iostream>
#include <map>
#include <string>
#include <vector>
 
struct Foo
{
    std::vector<int> mem = {1, 2, 3}; // list-initialization of a non-static member
    std::vector<int> mem2;
 
    Foo() : mem2{-1, -2, -3} {} // list-initialization of a member in constructor
};
 
std::pair<std::string, std::string> f(std::pair<std::string, std::string> p)
{
    return {p.second, p.first}; // list-initialization in return statement
}
 
int main()
{
    int n0{};  // value-initialization (to zero)
    int n1{1}; // direct-list-initialization
 
    std::string s1{'a', 'b', 'c', 'd'}; // initializer-list constructor call
    std::string s2{s1, 2, 2};           // regular constructor call
    std::string s3{0x61, 'a'}; // initializer-list ctor is preferred to (int, char)
 
    int n2 = {1}; // copy-list-initialization
    double d = double{1.2}; // list-initialization of a prvalue, then copy-init
    auto s4 = std::string{"HelloWorld"}; // same as above, no temporary
                                         // created since C++17
 
    std::map<int, std::string> m = // nested list-initialization
    {
        {1, "a"},
        {2, {'a', 'b', 'c'}},
        {3, s1}
    };
 
    std::cout << f({"hello", "world"}).first // list-initialization in function call
              << '\n';
 
    const int (&ar)[2] = {1, 2}; // binds an lvalue reference to a temporary array
    int&& r1 = {1}; // binds an rvalue reference to a temporary int
//  int& r2 = {2}; // error: cannot bind rvalue to a non-const lvalue ref
 
//  int bad{1.0}; // error: narrowing conversion
    unsigned char uc1{10}; // okay
//  unsigned char uc2{-1}; // error: narrowing conversion
 
    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); // has effect of [[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 enumの直接リスト初期化で、あまりにも多くのソース型が許容されていました。 制限された
CWG 2627 C++11 より大きな整数型の狭いビットフィールドはより小さな整数型に昇格できるが、
それでも縮小変換とされていました。
それは
縮小変換ではない
CWG 2713 C++20 集約クラスへの参照が
指定初期化子リストによって初期化できませんでした。
許可
CWG 2830 C++11 リスト初期化はトップレベルのcv修飾子を無視しませんでした。 無視する
CWG 2864 C++11 オーバーフローする浮動小数点変換は縮小変換ではありませんでした。 それらは縮小変換です
P1957R2 C++11 ポインタ/ポインタ・トゥ・メンバからの変換
からboolへの変換は縮小変換ではありませんでした。
縮小変換と見なされる
P2752R3 C++11 寿命が重なるバッキング配列は重なることができませんでした。 それらは重なる場合があります

[編集] 関連項目

English 日本語 中文(简体) 中文(繁體)