C++11 Universal Initialization は、いつでも使うべきなのか

答えは、現状 "No." です。

ではいつ使うべきで、いつ使わないべきか？
以下の記事を元ネタに、まとめてみます。

 - [Is C++11 Uniform Initialization a replacement for the old style syntax?](http://programmers.stackexchange.com/questions/133688/is-c11-uniform-initialization-a-replacement-for-the-old-style-syntax) ---  StackExchange
 - [When to use the brace-enclosed initializer?](http://stackoverflow.com/questions/9976927/when-to-use-the-brace-enclosed-initializer) --- StackOverflow
 - [Why can't I initialize a reference in an initializer list with uniform initialization?](http://stackoverflow.com/questions/10509603/why-cant-i-initialize-a-reference-in-an-initializer-list-with-uniform-initializ) --- StackOverflow
 - [Uniform initialization syntax and semantics](http://www.stroustrup.com/C++11FAQ.html#uniform-init) --- Stroustrup.com

# 1. はじめに｜Universal Initialization とは？

C++11 からは `int` 変数でも、配列でも、構造体でも、`std::vector` のようなSTLコンテナでも、同様の見た目で初期化できます。例えば、昔ながらの書き方では・・・

```cpp:昔ながらの初期化の書き方
int x = 3;    // "=" を使って。

int a[] = { 0, 1, 2, 3 };   // "= {}" を使って。

struct S1 { int a, b; } s = { 0, 1 };   // これも "= {}" を使って。

std::vector<int> v;
for(int i = 0; i < 4; ++i) v.push_back(a[i]);  // ループを使って。
```

初期化する対象によってバラバラですが、C++11なら・・・

```cpp:モダンで統一的な書き方
int x { 3 };
int a[] { 0, 1, 2, 3 };
struct S1 { int a, b; } s { 0, 1 };
std::vector<int> v { 0, 1, 2, 3 };
```

この `{` ... `}` を使った統一的な書き方は、ゆえに **Universal Initialization**、または **Uniform Initialization** と呼ばれます。Bjarne Stroustrup 推奨の初期化方法だそうです。


# 2. 有用なケース

## 2.1 コンテナや配列を要素で初期化

この使い方が、`{...}` の使い方の王道でしょうか。

```cpp:古い書き方
std::vector<int> v;
for(int i = 0; i < 4; ++i) v.push_back(a[i]);

int a[] = { 0, 1, 2, 3 }; 
```

```cpp:モダンな書き方
std::vector<int> v { 0, 1, 2, 3 };

int a[] { 0, 1, 2, 3 }; 
```

新しい書き方はダイレクトで、見た目がわかりやすくなった。そして、オペレータ `=` は **右辺のオブジェクトを左辺にコピー** する意味合いがあるので、`a[]` の初期化に `=` を使わない点も、より直感とマッチするように思います。



## 2.2 冗長な戻り値の型を省略できる

構造体を返す関数での使い方。

```cpp:古い書き方
vec3 GetValue()
{
  // ここで x,y,z を計算する 
  return vec3(x, y, z);
}
```

```cpp:モダンな書き方
vec3 GetValue()
{
  // ここで x,y,z を計算する 
  return {x, y, z};  // vec3{x,y,z}で生成されたオブジェクトが返る
}
```

関数の最初で `vec3` と言っているのだから、`return` で再び `vec3` というのは冗長という考え。

ただし、巨大な関数では最初の `vec3` が視界の外に行ってしまい、可読性が落ちるという意見もあるようです。

また、昔ながらの構造体ではなく、コンストラクタに `explicit` が付いているクラスでは、この暗黙キャストは拒否られてコンパイルできません。

```cpp:コンパイルエラーで残念
std::tuple<int,char> ReturnMySpecialTuple(void)
{
  return { 1, 'a' };  // 暗黙キャストは許すまじ; Compile error
}
```

普通、`explicit` をコンストラクタに付けるのは、そのコンストラクタが、暗黙キャストのためにうっかり呼ばれるのを防止するため。

例えば・・・

```cpp:意味不鮮明な暗黙キャスト
// The C++ Programming Language 4th ed
// 16.2.6 explicit Constructors より引用（一部改変）

class Date {
  int d, m, y;
public:
  Date(int d_=today.d, int m_=today.m, int y_=today.y)
    : d(d_), m(m_), y(y_) {} // 良い子は値をチェックしてね
}

void DoSomethingOnDate(Date d)
{
  // ... なにか価値あることを提供 ...
}

DoSomethingOnDate(15);  // 15 なんのこっちゃ。
Date d = 15;  // 難読性コーディングのつもり？
```

`explicit` をつけることで、コンパイラが不束者をキャッチします。

```cpp:意味不鮮明な暗黙キャストを抑止するexplicit
class Date {
  int d, m, y;
public:
  explicit Date(int d_=today.d, int m_=today.m, int y_=today.y)
    : d(d_), m(m_), y(y_) {} // 良い子は値をチェックしてね
}

DoSomethingOnDate(15);  // Compile error
Date d = 15;  // Compile error
Date d(15);  // 明示的な初期化だから、今まで通り可能（第一引数 d_ = 15）
```

このように仕組みだけ見ると有用な `explicit` ですが、歴史的経緯により、一部クラスに対して、余計な `explicit` がついてしまったようです。

変な挙動に巻き込まれ時間を無駄にしないためには、戻り値に暗黙キャストの `{...}` を使うのは、昔ながらの構造体のみにした方が良いかもしれない。



# 3. 使わない方が無難なケース

## 3.1 要素1個でのコンテナ初期化

混乱を招きがちな使い方。特に以下の３パターンなど。

```cpp:要素1個でのコンテナ初期化
std::vector<int> v(10);    // サイズ10の int のベクタ、全要素 0
std::vector<int> w{10};    // サイズ1の int のベクタ、最初の要素 10
std::vector<std::string> s{10};  // サイズ10の string のベクタ、全要素は空
```

一行目は明らか。昔からあるサイズ指定のコンストラクタが呼ばれる。

二行目は *Universal Initialization* のお陰で、直接要素を与えての初期化されます。C++11 では `std::vector` のコンストラクタに`initializer_list<value_type>` を第一引数に取るものが定義されており、これを呼び出します。今回、たまたま１要素しかなかったため、引数１個のコンストラクタ（一行目）と見た目が近くなってしまった。

三行目は、*Universal Initialization* のつもりだったかもしれないが、`int` で `string` の要素は初期化できない。コンパイラは「`int` はサイズのつもりじゃね？」と、サイズ指定のコンストラクタと結びつけてしまう。残念なことに **コンパイルエラーにならず**、実行時に初めて間違いに気づくかも知れません。普通しない類の間違いですが、`int`と`string`ではなく、もっと複雑なクラス階層だとミスに気づかないかもしれない。

### 【混沌の原因】

この混沌は `{...}` が、これまでのコンストラクタと`initializer_list<value_type>` を引数にとる新種のコンストラクタと、どちらも呼び出す可能性がある（引数次第）という特性に起因しています。

故に、通常のコンストラクタと`initializer_list`のコンストラクタと、両方を考慮しなければならない **被りパターン** では、`{...}` の利用を避けたほうがバグが少なくなるかもしれません。

```cpp:被りパターンを避ける｜２行だけど、こちらの方が可読性良い？
std::vector<int> v(10);    // サイズ10の int のベクタ、全要素 0
v[0] = 0;   // 最初の要素のみの初期化
```

もしくは、自分のクラスなら、`initializer_list`のコンストラクタを書かなければ、被りパターンも発生しえないので `{...}` も安心です。


## 3.2 「明らかに要素」でないものによる初期化

`{...}` は普通のコンストラクタも呼び出せるので、要素でない値を元にした初期化もできます。例えば、前述のコンテナサイズがそうでした。

しかし、元々 `{...}` は「配列や構造体の中身を列挙して初期化」する文法です。それ以外のデータを元にオブジェクトを構築するのは、むしろ、`(...)` を使ったこれまでのコンストラクタの本来の機能です。そこに `{...}` を持ち込むのは混乱の元。

```cpp:要素の列挙による初期化と、そうでないもの
std::string good ("abcde", 3);  // OK! 昔ながらのコンストラクタ
std::string pretty { 'a', 'b', 'c' }; // OK! 要素による初期化
std::string bad { "abcde", 3 };  // OMG！ 要素じゃない
```

この例は単純な `std::string` だったので深刻さはありませんが、もっと複雑なケースだと辛くなってきます。

元ネタの記事に、以下の例がありました。

```cpp:駄目なmake_uniqueの実装例
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{
  return std::unique_ptr<T> {
    new T { std::forward<Args>(args)... } // argsは要素？それ以外？
  };
}

auto q = make_unique<std::vector<string>>(20, "x"); // string 20個分 {"x","x",...}
auto p = make_unique<std::vector<int>>(20, -1); // int 2個分！ {20,-1}
```

「要素による初期化」であると自明でないなら、昔ながらに `(...)` を使う方が安全かつ安心。という方針で書き改めてみます。

```cpp:ベターなmake_uniqueの実装例
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{
  return std::unique_ptr<T> {   // 要素の列挙による初期化なので {...}
    new T (std::forward<Args>(args)...)   // 昔ながらのコンストラクタ
  };
}

auto q = make_unique<std::vector<string>>(20, "x"); // string 20個分
auto p = make_unique<std::vector<int>>(20, -1); // int 20個分 :-)
```

期待通りの、統一された振る舞いになりました。


## 3.3 引数の型を省略

構造体を引数に取る関数で、型を省略するのに `{...}` を使う用途です。

```cpp:古い書き方
double norm(const std::complex<double> &c)
{ return c.real()*c.real() + c.imag()*c.imag(); }

std::complex<double> two_plus_twoi {2.0,2.0};
std::cout << norm(two_plus_twoi);  // OK! "8" を表示
```

```cpp:モダンな書き方
double norm(const std::complex<double> &c)
{ return c.real()*c.real() + c.imag()*c.imag(); }

std::cout << norm({2.0,2.0});  // OK! "8" を表示
```

要素を列挙して、暗黙キャストで一時オブジェクト `2.0+2.0i` を作り、関数に渡しています。楽になって良いじゃない。しかし、以下のコードはうまく行きません。

```cpp:残念な暗黙キャスト
std::cout << std::norm({2.0,2.0});   // NG! コンパイルエラー
```

原因は `std::norm` が `std::norm(const std::complex<T> &c)` という風にテンプレート定義されており `T == double` の推定に失敗するから。南無三。

ある関数の実装が「テンプレートかそうでないか」というのは、利用側は意識しないで済む方が良いので、`{...}` による暗黙キャストでトラブルに巻き込まれるより、現状は利用を避けるほうが良いでしょう。

また、開発途中で API が流動的だったり、色々と多重定義されている場合、`{...}` がたまたま希望と違うコンストラクタにマッチし、うっかりコンパイル通ってしまう危険性もあります。

暗黙のキャストは「絶対明らか」な場合以外、避けた方が良いと思います。


## 3.3 参照変数の初期化

以下のコードは、VisualStudio 2013 では予想通りの動作ですが、CentOS7.1 デフォルトの gcc 4.8 ではコンパイルできませんでした。

```cpp:問題あるコード
std::vector<int> v(10);   // 10要素を持つ int のベクタ
int &t{v[3]};   // 4要素目に x という名前を付ける: compile error
```

参照変数は、当面、昔ながらに書いたほうが平和に暮らせそう。

```cpp:昔ながらの書き方
std::vector<int> v(10);   // 10要素を持つ int のベクタ
int &t = v[3];   // 4要素目に x という名前を付ける: OK!
```

クラスのコンストラクタでは `=` は利用できないので `(...)` ですね。

```cpp:クラス内の参照変数を初期化
class S {
    int &x;
public:
    S(int &x_) : x(x_) {}    // 昔ながらの参照変数の初期化
    int get(void) { return x; }
};

int x = 0;
S s(x);
std::cout << "x:" << s.get() << std::endl;  // "0" を表示
x = 1;
std::cout << "x:" << s.get() << std::endl;  // "1" を表示
```


# 4. 結論｜ガイドライン

結局、いつ `{...}` を使うべきか。
StackOverflow にあったガイドラインを、自分流にアレンジしてみました。

| 対象 | 初期化のパターン |
|------|---------------|
| 昔ながらの配列 | 要素で初期化は `{...}`でOK |
| 昔ながらの構造体 | 要素で初期化は `{...}`でOK |
| 昔ながらのクラス | 要素で初期化するなら `{...}` |
|           | 要素以外で初期化するなら `(...)` |
| STLコンテナ | 要素複数で初期化するなら `{...}` |
|   | 要素１個なら `v[0] = value` 等がベター |
| `initializer_list` コンストラクタを持つクラス一般 | 他のコンストラクタと引数の数が被らない範囲の要素数なら `{...}` |
|   | 被るなら各要素を個別に初期化 |
|   | そもそも要素でないなら `(...)` で初期化すべし |
| 参照変数 | `t = ref`または `(...)` で初期化 |
| 戻り値オブジェクト | 要素を詰めて返すなら `{...}` |
|   | それ以外なら、型明示 `(...)` で初期化 |
| 引数オブジェクト | 型明示 `(...)` で初期化 |

さて、`int x` のような基本型はどちらがいいの？というのには触れていません。些細な違いはありますが、現状どちらでも良いように思います。

`int x { 0 }` の方が文法に統一感ありますが `int x = 0;` や `auto x = 0;` の方が見やすいという意見もありそう。ただ、`int x = {0}` は冗長に思います。そんなコード書く人、いるかわかりませんが。

基本型に関する自分向けガイドラインは、「どちらでも良い／既存コードのスタイルに合わせる。」としておきたいと思います。

以上です。