---
# System prepended metadata

title: 指示付き初期化
tags: [elkurin-daily-note, C++-General]

---

# 指示付き初期化
C++20からメンバの名前を指定して初期化ができるようになった。

以下のような感じ。
```cpp=
WaylandWindow::WindowStates window_states{.is_maximized = true,
                                          .is_activated = true};
```
WindowStatesという型の中で`is_maximized`という変数にtrueを、`is_activated`という変数にtrueを入れている。  
デフォルト引数を持ったコンストラクタに突っ込むのと違って、最初の方の値は全部書かないといけないことはなく、ただ名前を指定して好きなところに指定したい値だけいれることができる。  
指定されなかったやつはデフォルト値が入る。

このときの`.is_maximized`をdesignatorとよび、{.is_maximized = true}といった初期化の仕方を`designated initializer list`という。

## いつ使える？
この指示付き初期化フォーマットはあらゆるオブジェクトの初期化に使えるわけではない。  
いろんな変数名の子供を持つ型として、classやstruct、unionなどがある。これらについてどんな条件で使えるのか確認する。

波括弧での一括初期化はaggregateな型でないとだめだが、このaggregateの定義はC++のバージョンごとに変わっている。

まずコンストラクタについて。  
`C++11`まではuser-declaredなコンストラクタがなければ良い。  
`C++20`まではuser-provided, inherited or explictなコンストラクタがなければ良い。  
`C++20`以降はuser-declared or inheritedなコンストラクタが泣けえれば良い。

なぜこういう変遷を遂げたのかは置いておいて、今回は指示付き初期化の条件を追うので、それが導入されたC++20以降を確認すれば十分。  
ところでChromiumではdefaultでもメンバ変数が10くらいになるとコンストラクタを宣言しないといけないスタイルが導入されているせいで、designated initializerが使えず、既存の使用を直さないといけない。うざい。

次にメンバについて。  
すべてpublicに宣言されていないとダメで、private/protectedなメンバ変数がいればアウト。  
なおstatic dataであれば初期化リストに関わらないのでセーフ。

また継承にも制約がある。  
aggregateの定義としては、`C++17`まではbase classがないことが条件。  
`C++17`以降はvirtualなbase classがなく、かつprivate/protectedで継承していないことが条件。これもなんで変わったのかは保留。  
またvirtual関数が含まれていないことも必要。

`C++14`まではdefault member initializer、つまり
```cpp=
struct A {
  std::string s = "hello";
};
```
のようにデフォルトでメンバ変数の値を代入しているとaggregatedではなかったらしいが、これは削除。  
これがダメだと指示付き初期化の意味がほぼなさそう。

以上が条件。

クラスは複雑すぎることが多いが、Structは使いやすそうだし共用体も使える。
```cpp=
union u {
  int a;
  const char* b;
};

u a = { .a = 1 };
u b = { .b = "hello" };
// ERROR: 共用体なので2個宣言したらエラー。
u c = { .a = 1, .b = "hello"};
```

### user-declared / user-provided
ちなみにuser-declaredはただ関数の宣言を行うだけだが、user-providedは`=delete`か`=default`で定義されていないかまで加味する。  
`A::A() = default;`はuser-declaredだがuser-providedではないコンストラクタということ。

## 初期化の仕方
初期化の仕方にも注意点がある。  
```cpp=
struct Point3D { int x; int y; int z = 0; };
struct Rect { Point3D p1; Point3D p2; };

// ERROR: 順番が違うとエラー
Point3D p {.z = 3, .y = 2, .x = 1};

// ERROR: .a.b 的なのはダメ
Rect r1 { .p1.x = 1, .p1.y = 2, .p1.z = 3 }; 
// PASSED: ネストしてかけばOK
Rect r2 { .p1{.x = 1, .y = 2, .z = 3} };
```