集成体初期化
集約体を初期化子リストから初期化します。リスト初期化の一種です(C++11以降)。
目次 |
[編集] 構文
T object = { arg1, arg2, ... }; |
(1) | ||||||||
T object { arg1, arg2, ... }; |
(2) | (C++11以降) | |||||||
T object = { .des1 = arg1 , .des2 { arg2 } ... }; |
(3) | (C++20以降) | |||||||
T object { .des1 = arg1 , .des2 { arg2 } ... }; |
(4) | (C++20以降) | |||||||
[編集] 定義
[編集] 集約体
集約体は、以下のいずれかの型です。
- 配列型
- 以下のプロパティを持つクラス型
|
(C++11まで) |
| (C++11以降) (C++20まで) | |
|
(C++20以降) |
- プライベートまたはプロテクトされた直接の非静的データメンバーがない
|
(C++17まで) |
| (C++17以降) |
- 仮想メンバー関数がない
| (C++11以降) (C++14まで) |
[編集] 要素
集約体の要素は、次のとおりです。
- 配列の場合、添字の昇順での配列要素、または
|
(C++17まで) |
| (C++17以降) |
[編集] 帰属
波括弧で囲まれた初期化子リスト内の各初期化子句は、初期化される集約体の要素、またはそのサブ集約体の要素に帰属すると言われます。
初期化子句のシーケンスと、初期化される集約体の要素のシーケンスとして最初に形成され、以下に説明するように変更される可能性がある集約要素のシーケンスを考慮すると
- 各初期化子句について、以下の条件のいずれかが満たされる場合、それは対応する集約要素elemに帰属します。
- elemは集約体ではありません。
- 初期化子句は{で始まります。
- 初期化子句は式であり、式をelemの型に変換する暗黙の変換シーケンスを形成できます。
- elemはそれ自体が集約要素を持たない集約体です。
- そうでない場合、elemは集約体であり、そのサブ集約体は集約要素のリストでそれ自身の集約要素のシーケンスに置き換えられ、帰属分析は最初のそのような要素と同一の初期化子句で再開されます。言い換えれば、これらのルールは集約体のサブ集約体に再帰的に適用されます。
すべての初期化子句が使い果たされると、分析は完了します。集約体の要素またはそのサブ集約体のいずれにも帰属しない初期化子句が残っている場合、プログラムは不正です。
struct S1 { int a, b; }; struct S2 { S1 s, t; }; // Each subaggregate of “x” is appertained to an initializer clause starting with { S2 x[2] = { // appertains to “x[0]” { {1, 2}, // appertains to “x[0].s” {3, 4} // appertains to “x[0].t” }, // appertains to “x[1]” { {5, 6}, // appertains to “x[1].s” {7, 8} // appertains to “x[1].t” } }; // “x” and “y” have the same value (see below) S2 y[2] = {1, 2, 3, 4, 5, 6, 7, 8}; // The process of the appertainment analysis of “y”: // 1. Initializes the aggregate element sequence (x[0], x[1]) and // the initializer clause sequence (1, 2, 3, 4, 5, 6, 7, 8). // 2. Starting from the first elements of each sequence, // checks whether 1 appertains to x[0]: // · x[0] is an aggregate. // · 1 does not begin with {. // · 1 is an expression, but it cannot be implicitly converted to S2. // · x[0] has aggregate elements. // 3. 0 cannot appertain to x[0], therefore x[0] is replaced by x[0].s and x[0].t, // the aggregate element sequence becomes (x[0].s, x[0].t, x[1]). // 4. Resumes the appertainment check, but 1 cannot appertain to x[0].s either. // 5. The aggregate element sequence now becomes (x[0].s.a, x[0].s.b, x[0].t, x[1]). // 6. Resumes the appertainment check again: // 1 appertains to x[0].s.a, and 2 appertains to x[0].s.b. // 7. The rest of the appertainment analysis works similarly. char cv[4] = {'a', 's', 'd', 'f', 0}; // Error: too many initializer clauses
[編集] 初期化プロセス
[編集] 要素の種類の決定
集約初期化の効果は次のとおりです。
|
(C++20以降) |
- それ以外の場合、初期化子リストが空でない場合、集約体の明示的に初期化された要素は、帰属する初期化子句を持つ要素と、帰属する初期化子句を持つサブ集約体を持つ要素です。
- それ以外の場合、初期化子リストは空でなければならず({})、明示的に初期化された要素はありません。
- 集約体が共用体であり、2つ以上の明示的に初期化された要素がある場合、プログラムは不正です。
union u { int a; const char* b; }; u a = {1}; // OK: explicitly initializes member `a` u b = {0, "asdf"}; // error: explicitly initializes two members u c = {"asdf"}; // error: int cannot be initialized by "asdf" // C++20 designated initializer lists u d = {.b = "asdf"}; // OK: can explicitly initialize a non-initial member u e = {.a = 1, .b = "asdf"}; // error: explicitly initializes two members
[編集] 明示的に初期化された要素
明示的に初期化された各要素について
struct C { union { int a; const char* p; }; int x; } c = {.a = 1, .x = 3}; // initializes c.a with 1 and c.x with 3
|
(C++20以降) |
|
(C++20まで) |
|
(C++20以降) |
- 初期化子句が集約要素に帰属する場合、集約要素は初期化子句からコピー初期化されます。
- そうでない場合、集約要素は、集約要素のサブオブジェクトに帰属するすべての初期化子句で構成される波括弧で囲まれた初期化子リストから、出現順にコピー初期化されます。
struct A { int x; struct B { int i; int j; } b; } a = {1, {2, 3}}; // initializes a.x with 1, a.b.i with 2, a.b.j with 3 struct base1 { int b1, b2 = 42; }; struct base2 { base2() { b3 = 42; } int b3; }; struct derived : base1, base2 { int d; }; derived d1{{1, 2}, {}, 4}; // initializes d1.b1 with 1, d1.b2 with 2, // d1.b3 with 42, d1.d with 4 derived d2{{}, {}, 4}; // initializes d2.b1 with 0, d2.b2 with 42, // d2.b3 with 42, d2.d with 4
[編集] 暗黙的に初期化された要素
非共用体集約体の場合、明示的に初期化された要素ではない各要素は次のように初期化されます。
|
(C++11以降) |
- それ以外の場合、要素が参照でない場合、要素は空の初期化子リストからコピー初期化されます。
- それ以外の場合、プログラムは不適格となります。
struct S { int a; const char* b; int c; int d = b[a]; }; // initializes ss.a with 1, // ss.b with "asdf", // ss.c with the value of an expression of the form int{} (that is, 0), // and ss.d with the value of ss.b[ss.a] (that is, 's') S ss = {1, "asdf"};
集約体が共用体であり、初期化子リストが空の場合、
|
(C++11以降) |
- それ以外の場合、共用体の最初のメンバー(もしあれば)は空の初期化子リストからコピー初期化されます。
[編集] 境界不明の配列
波括弧で囲まれた初期化子リストで初期化される境界不明の配列の要素数は、配列の明示的に初期化された要素の数です。境界不明の配列は{}で初期化することはできません。
int x[] = {1, 3, 5}; // x has 3 elements struct Y { int i, j, k; }; Y y[] = {1, 2, 3, 4, 5, 6}; // y has only 2 elements: // 1, 2 and 3 appertain to y[0], // 4, 5 and 6 appertain to y[1] int z[] = {} // Error: cannot declare an array without any element
指示付き初期化子構文形式(3,4)は指定初期化子として知られています。各designatorはTの直接非静的データメンバーを命名する必要があり、式で使用されるすべてのdesignatorはTのデータメンバーと同じ順序で出現する必要があります。 struct A { int x; int y; int z; }; A a{.y = 2, .x = 1}; // error; designator order does not match declaration order A b{.x = 1, .z = 2}; // ok, b.y initialized to 0 指定初期化子によって命名された各直接非静的データメンバーは、指定子の後に続く対応する波括弧または等号初期化子から初期化されます。縮小変換は禁止されています。 指定初期化子を使用して、共用体を最初の状態以外の状態に初期化できます。共用体には1つの初期化子のみを指定できます。 union u { int a; const char* b; }; u f = {.b = "asdf"}; // OK, active member of the union is b u g = {.a = 1, .b = "asdf"}; // Error, only one initializer may be provided 非共用体集約体の場合、指定初期化子が提供されていない要素は、初期化子句の数がメンバーの数より少ない場合について上述したのと同じように初期化されます(提供されている場合はデフォルトメンバー初期化子、それ以外の場合は空のリスト初期化)。 struct A { string str; int n = 42; int m = -1; }; A{.m = 21} // Initializes str with {}, which calls the default constructor // then initializes n with = 42 // then initializes m with = 21 指定初期化子句で初期化される集約体が匿名共用体メンバーを持つ場合、対応する指定初期化子はその匿名共用体のメンバーのいずれかを命名する必要があります。 注:順不同の指定初期化、ネストされた指定初期化、指定初期化子と通常の初期化子の混在、配列の指定初期化はすべてCプログラミング言語でサポートされていますが、C++では許可されていません。 struct A { int x, y; }; struct B { struct A a; }; struct A a = {.y = 1, .x = 2}; // valid C, invalid C++ (out of order) int arr[3] = {[1] = 5}; // valid C, invalid C++ (array) struct B b = {.a.x = 0}; // valid C, invalid C++ (nested) struct A a = {.x = 1, 2}; // valid C, invalid C++ (mixed) |
(C++20以降) |
[編集] 文字配列
通常の文字型(char、signed char、unsigned char)、char8_t(C++20以降)、char16_t、char32_t(C++11以降)、またはwchar_tの配列は、通常の文字列リテラル、UTF-8文字列リテラル(C++20以降)、UTF-16文字列リテラル、UTF-32文字列リテラル(C++11以降)、またはワイド文字列リテラルから、オプションで波括弧で囲んで初期化できます。さらに、charまたはunsigned charの配列は、オプションで波括弧で囲まれたUTF-8文字列リテラルによって初期化されることがあります(C++20以降)。文字列リテラル(暗黙の終端ヌル文字を含む)の連続する文字は、配列の要素を初期化します。必要に応じて、ソース値と宛先値に対して整数変換が行われます(C++20以降)。配列のサイズが指定されており、それが文字列リテラルの文字数よりも大きい場合、残りの文字はゼロ初期化されます。
char a[] = "abc"; // equivalent to char a[4] = {'a', 'b', 'c', '\0'}; // unsigned char b[3] = "abc"; // Error: initializer string too long unsigned char b[5]{"abc"}; // equivalent to unsigned char b[5] = {'a', 'b', 'c', '\0', '\0'}; wchar_t c[] = {L"кошка"}; // optional braces // equivalent to wchar_t c[6] = {L'к', L'о', L'ш', L'к', L'а', L'\0'};
[編集] 注釈
集約クラスまたは配列は、非集約型のpublic基底クラス(C++17以降)、メンバー、または要素を含むことができ、これらは上記で説明したとおりに初期化されます(例:対応する初期化子句からのコピー初期化)。
C++11までは、集約初期化で縮小変換が許可されていましたが、現在は許可されていません。
C++11までは、構文の制限により、集約初期化は変数定義でのみ使用でき、コンストラクタ初期化子リスト、new式、または一時オブジェクトの作成では使用できませんでした。
C言語では、文字列リテラルのサイズよりも1つ少ないサイズの文字配列は文字列リテラルから初期化できますが、結果の配列はヌル終端されません。これはC++では許可されていません。
| 機能テストマクロ | 値 | 規格 | 機能 |
|---|---|---|---|
__cpp_aggregate_bases |
201603L |
(C++17) | 基底クラスを持つ集約クラス |
__cpp_aggregate_nsdmi |
201304L |
(C++14) | デフォルトメンバー初期化子を持つ集約クラス |
__cpp_aggregate_paren_init |
201902L |
(C++20) | 直接初期化の形式での集約初期化 |
__cpp_char8_t |
202207L |
(C++23) (DR20) |
char8_tの互換性と移植性の修正(UTF-8文字列リテラルからunsigned char配列を初期化することを許可) |
__cpp_designated_initializers |
201707L |
(C++20) | 指示付き初期化子 |
[編集] 例
#include <array> #include <cstdio> #include <string> struct S { int x; struct Foo { int i; int j; int a[3]; } b; }; int main() { S s1 = {1, {2, 3, {4, 5, 6}}}; S s2 = {1, 2, 3, 4, 5, 6}; // same, but with brace elision S s3{1, {2, 3, {4, 5, 6}}}; // same, using direct-list-initialization syntax S s4{1, 2, 3, 4, 5, 6}; // error until CWG 1270: // brace elision only allowed with equals sign int ar[] = {1, 2, 3}; // ar is int[3] // char cr[3] = {'a', 'b', 'c', 'd'}; // too many initializer clauses char cr[3] = {'a'}; // array initialized as {'a', '\0', '\0'} int ar2d1[2][2] = {{1, 2}, {3, 4}}; // fully-braced 2D array: {1, 2} // {3, 4} int ar2d2[2][2] = {1, 2, 3, 4}; // brace elision: {1, 2} // {3, 4} int ar2d3[2][2] = {{1}, {2}}; // only first column: {1, 0} // {2, 0} std::array<int, 3> std_ar2{{1, 2, 3}}; // std::array is an aggregate std::array<int, 3> std_ar1 = {1, 2, 3}; // brace-elision okay // int ai[] = {1, 2.0}; // narrowing conversion from double to int: // error in C++11, okay in C++03 std::string ars[] = {std::string("one"), // copy-initialization "two", // conversion, then copy-initialization {'t', 'h', 'r', 'e', 'e'}}; // list-initialization union U { int a; const char* b; }; U u1 = {1}; // OK, first member of the union // U u2 = {0, "asdf"}; // error: too many initializers for union // U u3 = {"asdf"}; // error: invalid conversion to int [](...) { std::puts("Garbage collecting unused variables... Done."); } ( s1, s2, s3, s4, ar, cr, ar2d1, ar2d2, ar2d3, std_ar2, std_ar1, u1 ); } // aggregate struct base1 { int b1, b2 = 42; }; // non-aggregate struct base2 { base2() : b3(42) {} int b3; }; // aggregate in 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
出力
Garbage collecting unused variables... Done.
[編集] 欠陥報告
以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。
| DR | 適用対象 | 公開された動作 | 正しい動作 |
|---|---|---|---|
| CWG 413 | C++98 | 匿名ビットフィールドは集約初期化で初期化されていました | それらは無視されます |
| CWG 737 | C++98 | 文字配列が文字列リテラルで初期化されたとき 配列サイズより文字数が少ない場合、文字 末尾の'\0'の後の要素は未初期化でした |
現在は ゼロ初期化される |
| CWG 1270 | C++11 | 波括弧省略はコピーリスト初期化でのみ許可されていました | 他の場所でも許可される |
| CWG 1518 | C++11 | 明示的なデフォルトコンストラクタを宣言するクラスまたは 継承されたコンストラクタを持つクラスが集約体となるべきでした |
それは 集約体ではありません |
| CWG 1622 | C++98 | 共用体は{}で初期化できませんでした | 許可 |
| CWG 2149 (P3106R1) |
C++98 | 波括弧省略が 配列サイズの推論中に適用可能であるかどうかは不明確でした |
適用可能である |
| CWG 2272 | C++98 | 明示的に初期化されていない非静的参照メンバーは 空の初期化子リストからコピー初期化されていました |
この場合、プログラムは 不正となる |
| CWG 2610 | C++17 | 集約型はプライベートまたはプロテクトされた間接基底クラスを持つことができませんでした | 許可 |
| CWG 2619 | C++20 | 指定初期化子からの初期化の種類が不明確でした | それは初期化子の 種類に依存します |
| P2513R4 | C++20 | UTF-8文字列リテラルはcharまたは unsigned charの配列を初期化できませんでした。これはCまたはC++17と互換性がありませんでした。 |
そのような初期化は 有効です |
[編集] 関連項目
| C言語ドキュメント(構造体と共用体の初期化)
|