名前空間
変種
操作

集成体初期化

提供: cppreference.com
< cpp‎ | language
 
 
C++言語
一般的なトピック
フロー制御
条件付き実行文
繰り返し文 (ループ)
ジャンプ文
関数
関数宣言
ラムダ関数宣言
inline 指定子
例外指定 (非推奨)
noexcept 指定子 (C++11)
例外
名前空間
指定子
decltype (C++11)
auto (C++11)
alignas (C++11)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点
文字 - 文字列 - nullptr (C++11)
ユーザ定義 (C++11)
ユーティリティ
属性 (C++11)
typedef 宣言
型エイリアス宣言 (C++11)
キャスト
暗黙の変換 - 明示的な変換
static_cast - dynamic_cast
const_cast - reinterpret_cast
メモリ確保
クラス
クラス固有の関数特性
特別なメンバ関数
テンプレート
その他
 
 

波括弧初期化子リストから集成体を初期化します。

目次

[編集] 構文

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およびそれ以降)

[編集] 説明

集成体初期化はリスト初期化の形式のひとつであり、集成体を初期化します。

集成体は以下の型のいずれかです。

  • 配列型。
  • 以下の性質を満たすクラス型 (一般的には struct または union)。
  • private または protected な非静的データメンバを持たない。
  • ユーザ宣言されたコンストラクタを持たない。
(C++11以前)
  • ユーザ定義されたコンストラクタを持たない (明示的にデフォルト化または削除されたコンストラクタは許容されます)。
(C++11およびそれ以降)
(C++17以前)
  • ユーザ提供されたコンストラクタ、継承されたコンストラクタ、または explicit なコンストラクタを持たない (明示的にデフォルト化または削除されたコンストラクタは許容されます)。
(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 です。
  • 初期化子節の数がメンバの数より少ない、または初期化子リストが完全に空の場合、残りのメンバは値初期化されます。 参照型のメンバがそれら残りのメンバの中にある場合、プログラムは ill-formed です。
(C++11以前)
  • 初期化子節の数がメンバおよび基底 (C++17およびそれ以降)の数より少ない、または初期化子リストが完全に空の場合、残りのメンバおよび基底 (C++17およびそれ以降)は、クラス定義内で提供されていればそのデフォルト初期化子で、そうでなければ (C++14およびそれ以降)空のリストで、通常のリスト初期化のルールに従って初期化されます (すなわち、非クラス型およびデフォルトコンストラクタを持つ非集成体クラスの場合は値初期化を、集成体の場合は集成体初期化を行います)。 参照型のメンバがそれら残りのメンバの中にある場合、プログラムは ill-formed です。
(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およびそれ以降)

[編集] 文字配列

文字型 (charsigned charunsigned charchar16_tchar32_twchar_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++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]
//  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言語リファレンス