Aggregate initialization
初期化子リストからの集約の初期化 。これは list-initialization の一形態である (C++11以降) 。
目次 |
構文
T オブジェクト
= {
arg1, arg2, ...
};
|
(1) | ||||||||
T オブジェクト
{
arg1, arg2, ...
};
|
(2) | (C++11以降) | |||||||
T オブジェクト
= { .
des1
=
arg1
, .
des2
{
arg2
}
...
};
|
(3) | (C++20以降) | |||||||
T オブジェクト
{ .
des1
=
arg1
, .
des2
{
arg2
}
...
};
|
(4) | (C++20以降) | |||||||
定義
アグリゲート
aggregate は以下のいずれかの型です:
- array types
- class types that has
|
(C++11まで) |
|
(C++11以降)
(C++20まで) |
|
(C++20以降) |
-
- 非公開または保護された直接非静的データメンバーがないこと
|
(C++17まで) |
|
(C++17以降) |
-
- 仮想メンバ関数なし
|
(C++11以降)
(C++14まで) |
エレメント
集約の elements は以下の通りです:
- 配列の場合、配列要素は添字の昇順で、または
|
(C++17まで) |
|
(C++17以降) |
関連事項
各 initializer clause は、波括弧で囲まれた初期化リスト内で、初期化されるaggregateの要素、またはその副aggregateの要素のいずれかに appertain すると言われます。
初期化子句のシーケンス、および集約要素のシーケンス(最初に初期化される集約の要素シーケンスとして形成され、以下で説明するように潜在的に変更される)を考慮します:
- 各初期化子句について、以下のいずれかの条件が満たされる場合、それは対応する集成体要素 elem に帰属します:
-
- elem は集成体ではありません。
- 初期化子句が { で始まります。
- 初期化子句が式であり、その式から elem の型への 暗黙変換シーケンス が形成可能です。
- elem は、それ自体が集成体要素を持たない集成体です。
- それ以外の場合、 elem は集成体であり、その部分集成体は集成体要素のリスト内で自身の集成体要素のシーケンスで置き換えられ、帰属分析は最初のそのような要素と同じ初期化子句で再開されます。言い換えれば、これらの規則は集成体の部分集成体に対して再帰的に適用されます。
初期化節がすべて使い果たされた時点で解析は完了します。集約体の要素またはその副集約体のいずれにも属さない初期化節が残っている場合、プログラムは不適格となります。
struct S1 { long a, b; }; struct S2 { S1 s, t; }; // 「x」の各サブアグリゲートは、{ で始まる初期化節に帰属する S2 x[2] = { // 「x[0]」に帰属 { {1L, 2L}, // 「x[0].s」に帰属 {3L, 4L} // 「x[0].t」に帰属 }, // 「x[1]」に帰属 { {5L, 6L}, // 「x[1].s」に帰属 {7L, 8L} // 「x[1].t」に帰属 } }; // 「x」と「y」は同じ値を持つ(下記参照) S2 y[2] = {1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L}; // 「y」の帰属分析のプロセス: // 1. アグリゲート要素シーケンス (x[0], x[1]) と初期化節シーケンス (1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L) を初期化する // 2. 各シーケンスの最初の要素から開始し、1L が x[0] に帰属するかチェックする: // · x[0] はアグリゲートである // · 1L は { で始まらない // · 1L は式であるが、S2 に暗黙変換できない // · x[0] はアグリゲート要素を持つ // 3. 1L は x[0] に帰属できないため、x[0] は x[0].s と x[0].t に置き換えられ、 // アグリゲート要素シーケンスは (x[0].s, x[0].t, x[1]) となる // 4. 帰属チェックを再開するが、1L は x[0].s にも帰属できない // 5. アグリゲート要素シーケンスは (x[0].s.a, x[0].s.b, x[0].t, x[1]) となる // 6. 再度帰属チェックを再開: // 1L は x[0].s.a に帰属し、2L は x[0].s.b に帰属する // 7. 残りの帰属分析も同様に機能する char cv[4] = {'a', 's', 'd', 'f', 0}; // エラー:初期化節が多すぎる
初期化プロセス
要素種別の判定
集成体初期化の効果は以下の通りです:
|
(C++20以降) |
-
- それ以外の場合、 (C++20以降) 初期化子リストが空でない場合、集約の明示的に初期化された要素は、付属する初期化子節を持つ要素および付属する初期化子節を持つ部分集約を持つ要素である。
- それ以外の場合、初期化子リストは空でなければならない( { } )、そして明示的に初期化された要素は存在しない。
- アグリゲートが共用体であり、2つ以上の明示的に初期化された要素がある場合、プログラムは不適格です:
union u { int a; const char* b; }; u a = {1}; // OK: メンバー `a` を明示的に初期化 u b = {0, "asdf"}; // エラー: 2つのメンバーを明示的に初期化 u c = {"asdf"}; // エラー: int は "asdf" で初期化できない // C++20 指示付き初期化子リスト u d = {.b = "asdf"}; // OK: 非初期メンバーを明示的に初期化可能 u e = {.a = 1, .b = "asdf"}; // エラー: 2つのメンバーを明示的に初期化
明示的に初期化された要素
明示的に初期化された各要素について:
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以降) |
-
- 初期化句が集約要素に属する場合、その集約要素は初期化句から copy-initialized されます。
- それ以外の場合、集約要素は、その集約要素の部分オブジェクトに属する全ての初期化句からなる波括弧で囲まれた初期化リストから、出現順にcopy-initializedされます。
struct A { int x; struct B { int i; int j; } b; } a = {1, {2, 3}}; // a.xを1で、a.b.iを2で、a.b.jを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}; // 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++11以降) |
- それ以外の場合、要素が参照でない場合、要素は空の初期化子リストから copy-initialized されます。
- それ以外の場合、プログラムは不適格です。
struct S { int a; const char* b; int c; int d = b[a]; }; // ss.a を 1 で初期化し、 // ss.b を "asdf" で初期化し、 // ss.c を int{} 形式の式の値(つまり 0)で初期化し、 // ss.d を ss.b[ss.a] の値(つまり 's')で初期化する S ss = {1, "asdf"};
集約が共用体であり初期化子リストが空の場合、
|
(C++11以降) |
- それ以外の場合、共用体の最初のメンバ(存在すれば)が空の初期化子リストからコピー初期化されます。
境界が不明な配列
不明な境界を持つ配列が波括弧で囲まれた初期化子リストで初期化される場合、配列の要素数は明示的に初期化された要素の数となる。不明な境界を持つ配列は { } では初期化できない。
int x[] = {1, 3, 5}; // x は 3 つの要素を持つ struct Y { int i, j, k; }; Y y[] = {1, 2, 3, 4, 5, 6}; // y は 2 つの要素のみを持つ: // 1, 2, 3 は y[0] に属し、 // 4, 5, 6 は y[1] に属する int z[] = {} // エラー: 要素を持たない配列を宣言することはできない
指示付き初期化子構文形式 (3,4) は指示付き初期化子として知られています:各 designator はTの直接の非静的データメンバーを指定しなければならず、式で使用されるすべての designator はTのデータメンバーの順序と同じ順序で現れなければなりません。 struct A { int x; int y; int z; }; A a{.x = 1, .y = 2, .z = 3}; // ok A b{.y = 2, .z = 3, .x = 1}; // error; designator order does not match declaration order 指示付き初期化子によって指定された各直接非静的データメンバーは、designatorに続く対応する波括弧または等号初期化子から初期化されます。縮小変換は禁止されています。 指示付き初期化子は、 union を最初の状態以外の状態に初期化するために使用できます。unionに対して指定できる初期化子は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 非unionの集成体の場合、指示付き初期化子が指定されていない要素は、初期化子節の数がメンバーの数より少ない場合と同様に初期化されます(デフォルトメンバー初期化子が指定されている場合はそれを使用、それ以外の場合は空のリスト初期化): 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 struct A { int x; int y; int z; }; A a{.x = 1, .z = 2}; // ok, b.y initialized to 0 A b{.y = 2, .x = 1}; // error; designator order does not match declaration order A c{.y = 2}; // ok, c.x and c.z are initialized to 0 constexpr A d{.z = 2}; // can be used with constexpr, as opposed to: constexpr A d; static_assert(d.x == 0 && d.y == 0); // d.x and d.y are initialized to 0 指示付き初期化子節で初期化される集成体が匿名unionメンバーを持つ場合、対応する指示付き初期化子はその匿名unionのメンバーの1つを指定しなければなりません。 注:順不同の指示付き初期化、ネストされた指示付き初期化、指示付き初期化子と通常の初期化子の混合、および配列の指示付き初期化はすべて 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"; // 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'}; と同等
注記
集約クラスまたは配列は非集約の public bases (since C++17) を含むことができ、これらは上述の方法(例えば対応する初期化子節からのコピー初期化)で初期化されます。
C++11まで、集成体初期化では縮小変換が許可されていましたが、現在は許可されていません。
C++11まで、集成体初期化は変数定義でのみ使用可能であり、構文上の制限により、 コンストラクタ初期化子リスト や new式 、または一時オブジェクトの作成では使用できませんでした。
Cでは、文字列リテラルのサイズより1小さいサイズの文字配列を文字列リテラルで初期化できるが、結果の配列はnull終端されない。これは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 互換性と移植性の修正 ( ( unsigned char 配列の UTF-8文字列リテラル からの初期化を許可) |
__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}; // 同じだが、波括弧省略を使用 S s3{1, {2, 3, {4, 5, 6}}}; // 同じ、直接リスト初期化構文を使用 S s4{1, 2, 3, 4, 5, 6}; // CWG 1270までエラー: // 波括弧省略は等号でのみ許可 int ar[] = {1, 2, 3}; // arはint[3] // char cr[3] = {'a', 'b', 'c', 'd'}; // 初期化子が多すぎる char cr[3] = {'a'}; // 配列は{'a', '\0', '\0'}として初期化 int ar2d1[2][2] = {{1, 2}, {3, 4}}; // 完全な波括弧の2次元配列: {1, 2} // {3, 4} int ar2d2[2][2] = {1, 2, 3, 4}; // 波括弧省略: {1, 2} // {3, 4} int ar2d3[2][2] = {{1}, {2}}; // 最初の列のみ: {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ではOK std::string ars[] = {std::string("one"), // コピー初期化 "two", // 変換、その後コピー初期化 {'t', 'h', 'r', 'e', 'e'}}; // リスト初期化 union U { int a; const char* b; }; U u1 = {1}; // OK、共用体の最初のメンバ // U u2 = {0, "asdf"}; // エラー: 共用体に対する初期化子が多すぎる // U u3 = {"asdf"}; // エラー: intへの無効な変換 [](...) { std::puts("Garbage collecting unused variables... Done."); } ( s1, s2, s3, s4, ar, cr, ar2d1, ar2d2, ar2d3, std_ar2, std_ar1, u1 ); } // 集成体 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
出力:
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 documentation
for
Struct and union initialization
|
|
C documentation
の
Struct and union initialization
|