集成体初期化
波括弧初期化子リストから集成体を初期化します。
目次 |
[編集] 構文
T object = { arg1, arg2, ...};
|
(1) | ||||||||
T object { arg1, arg2, ...};
|
(2) | (C++11以上) | |||||||
T object = { . designator = arg1 , . designator { arg2 } ... };
|
(3) | (C++20以上) | |||||||
T object { . designator = arg1 , . designator { arg2 } ... };
|
(4) | (C++20以上) | |||||||
T object ( arg1, arg2, ...);
|
(5) | (C++20以上) | |||||||
[編集] 説明
集成体初期化は集成体を初期化します。 これはリスト初期化または直接初期化 (C++20以上)の形式のひとつです。 (C++11以上)
集成体は以下の型のいずれかです。
- 配列型。
- 以下の性質を満たすクラス型 (一般的には struct または union)。
- private または protected な直接の (C++17以上)非静的データメンバを持たない。
|
(C++11未満) |
|
(C++11以上) (C++17未満) |
|
(C++17以上) (C++20未満) |
|
(C++20以上) |
- virtual、private、または protected な (C++17以上)基底クラスを持たない。
- 仮想メンバ関数を持たない。
|
(C++11以上) (C++14未満) |
集成体初期化の効果は以下の通りです。
- 直接の public な基底、 (C++17以上)配列の要素、または非静的クラスメンバが、配列の添字の順またはクラス定義内での出現順で、初期化子リストの対応する節からコピー初期化されます。
- その初期化子節が式の場合は、コピー初期化の場合と同様、暗黙の変換が許容されます。 ただし、リスト初期化形式の場合は、縮小変換は禁止されます。 (C++11以上)
- その初期化子節がネストした波括弧初期化子リスト (式でない) の場合は、対応する配列の要素またはクラスのメンバまたは public な基底 (C++17以上)がその節からリスト初期化されます。 集成体初期化は再帰的です。
- そのオブジェクトがサイズの未知な配列であり、提供された波括弧で囲まれた初期化子に
n
個の節がある場合、その配列のサイズはn
です (この場合、オブジェクトは非静的データメンバであることはできないことに注意してください。 メンバは完全型でなければなりません)。
- 静的データメンバおよび無名ビットフィールドは集成体初期化では無視されます。
- 初期化子節の数が初期化するメンバおよび基底 (C++17以上)の数を超える場合、プログラムは ill-formed です。
|
(C++11未満) |
|
(C++11以上) |
- 共用体が集成体初期化によって初期化された場合、その最初の非静的データメンバのみが初期化されます。
集成体初期化がコピーリスト初期化の構文 (T a = {args..}) を使用する場合、 (C++14未満)ネストした初期化子リストの周りの波括弧は省略できます。 この場合、対応する部分集成体のすべてのメンバまたは要素を初期化するために必要なだけ初期化子節が使用され、後続の初期化子節はそのオブジェクトの後続のメンバを初期化するために使用されます。 しかし、そのオブジェクトがいかなるメンバも持たない部分集成体 (空の構造体や静的メンバのみを保持する構造体) を持つ場合、波括弧の省略は認められず、ネストした空のリスト {}
を使用しなければなりません。
指示付き初期化子構文の形式 (3,4) は指示付き初期化子と呼ばれます。 それぞれの designator は T の直接の非静的データメンバを表さなければならず、式で使用されるすべての designator は T のデータメンバと同じ順序で現れなければなりません。 struct A { int x; int y; int z; }; A a{.y = 2, .x = 1}; // エラー、指示子の順序が宣言の順序と一致しません。 A b{.x = 1, .z = 2}; // OK、 b.y は 0 に初期化されます。 指示付き初期化子によって表される直接の非静的データメンバは、それぞれ指示子の後の対応する波括弧または等号の初期化子から初期化されます。 縮小変換は禁止されています。 指示付き初期化子は共用体を最初以外の状態に初期化するために使用することができます。 ひとつの共用体につきひとつの初期化子のみ提供できます。 union u { int a; const char* b; }; u f = { .b = "asdf" }; // OK、共用体のアクティブメンバは b です。 u g = { .a = 1, .b = "asdf" }; // エラー、初期化子はひとつしか提供できません。 共用体でない集成体の場合、指示付き初期化子が提供されないメンバは、初期化子の数がメンバの数より少ないときについての説明と同じに初期化されます (提供されていればデフォルトメンバ初期化子、そうでなければ空のリスト初期化)。 struct A { string a; int b = 42; int c = -1; }; A{.c=21} // a を {} で初期化します。 つまりデフォルトコンストラクタを呼びます。 // そして b を = 42 で初期化します。 // そして c を = 21 で初期化します。 指示付き初期化子節で初期化された集成体が無名共用体メンバを持つ場合、対応する指示付き初期化子はその無名共用体のメンバのいずれかを表さなければなりません。 ノート: 順序が合っていない指示付き初期化、ネストした指示付き初期化、指示付き初期化子と通常の初期化子の混在、および配列の指示付き初期化は、いずれも C 言語ではサポートされていますが、 C++ では認められていません。 struct A { int x, y; }; struct B { struct A a; }; struct A a = {.y = 1, .x = 2}; // C では有効、 C++ では無効 (順序) int arr[3] = {[1] = 5}; // C では有効、 C++ では無効 (配列) struct B b = {.a.x = 0}; // C では有効、 C++ では無効 (ネスト) struct A a = {.x = 1, 2}; // C では有効、 C++ では無効 (混在) |
(C++20以上) |
[編集] 文字配列
文字型 (char
、 signed char
、 unsigned char
、 char8_t
、 char16_t
、 char32_t
、 wchar_t
) の配列は、適切な文字列リテラル (波括弧で囲っても構いません) から初期化できます。 文字列リテラルの連続する文字 (暗黙の終端のヌル文字を含みます) が、配列の要素を初期化します。 配列のサイズが指定されていて、それが文字列リテラルの文字数より大きい場合、残りの文字はゼロ初期化されます。
char a[] = "abc"; // char a[4] = {'a', 'b', 'c', '\0'}; と同等です。 // unsigned char b[3] = "abc"; // エラー、初期化子の文字列が長すぎます。 unsigned char b[5]{"abc"}; // unsigned char b[5] = {'a', 'b', 'c', '\0', '\0'}; と同等です。 wchar_t c[] = {L"кошка"}; // 波括弧で囲っても構いません。 // wchar_t c[6] = {L'к', L'о', L'ш', L'к', L'а', L'\0'}; と同等です。
[編集] ノート
集成体クラスまたは配列は、集成体でないパブリックな基底、 (C++17以上)メンバ、または要素を含むことができ、それらは上で説明した通りに初期化されます (例えば対応する初期化子節からコピー初期化されます)。
C++11 までは、集成体初期化における縮小変換が許されていましたが、現在は許されません。 ただし、 C++20 では、集成体初期化で丸括弧を使用するときは、許されます。
C++11 までは、集成体初期化は構文の制限によりコンストラクタの初期化子リストでは使用できませんでした。
C++14 までは、直接初期化の形式 T a {args..} は波括弧の省略が許されませんでした。
C では、文字列リテラルのサイズより1小さい文字配列をその文字列リテラルから初期化することができます (結果の配列はヌル終端されません)。 これは C++ では許されません。
[編集] 例
#include <string> #include <array> struct S { int x; struct Foo { int i; int j; int a[3]; } b; }; union U { int a; const char* b; }; int main() { S s1 = { 1, { 2, 3, {4, 5, 6} } }; S s2 = { 1, 2, 3, 4, 5, 6}; // 同上、波括弧が省略されています。 S s3{1, {2, 3, {4, 5, 6} } }; // 同上、直接リスト初期化の構文を使用しています。 S s4{1, 2, 3, 4, 5, 6}; // C++11 ではエラー、波括弧の省略は等号を用いる場合にのみ許されます。 // C++14 では OK。 int ar[] = {1,2,3}; // ar は int[3] です。 int ab[] (1, 2, 3); // (C++20) ab は int[3] です。 // char cr[3] = {'a', 'b', 'c', 'd'}; // 初期化子節が多すぎます。 char cr[3] = {'a'}; // {'a', '\0', '\0'} として初期化されます。 int ar2d1[2][2] = {{1, 2}, {3, 4}}; // 完全な波括弧付きの二次元配列: {1, 2} // {3, 4} int ar2d2[2][2] = {1, 2, 3, 4}; // 波括弧の省略: {1, 2} // {3, 4} int ar2d3[2][2] = {{1}, {2}}; // 第1カラムのみ: {1, 0} // {2, 0} std::array<int, 3> std_ar2{ {1,2,3} }; // std::array は集成体です。 std::array<int, 3> std_ar1 = {1, 2, 3}; // 波括弧省略可。 int ai[] = { 1, 2.0 }; // double から int への縮小変換。 // C++11 ではエラー、 C++03 では可。 std::string ars[] = {std::string("one"), // コピー初期化。 "two", // 変換後、コピー初期化。 {'t', 'h', 'r', 'e', 'e'} }; // リスト初期化。 U u1 = {1}; // OK、共用体の最初のメンバ。 // U u2 = { 0, "asdf" }; // エラー、共用体の初期化子が多すぎます。 // U u3 = { "asdf" }; // エラー、無効な int への変換。 } // 集成体 struct base1 { int b1, b2 = 42; }; // 非集成体 struct base2 { base2() : b3(42) {} int b3; }; // C++17 では集成体 struct derived : base1, base2 { int d; }; derived d1{ {1, 2}, { }, 4}; // d1.b1 = 1, d1.b2 = 2, d1.b3 = 42, d1.d = 4 derived d2{ { }, { }, 4}; // d2.b1 = 0, d2.b2 = 42, d2.b3 = 42, d2.d = 4
[編集] 関連項目
構造体と共用体の初期化 の C言語リファレンス
|