生配列、array、unique_ptr<T[]>、vector

C++ には、配列っぽいものが何個かある。
主要っぽい４つの配列っぽいものを比較してみる。

# 生配列

まずはC言語の伝統を汲む普通の配列。
使い方はこんな感じ：

```C++:C++11
// clang++ -std=c++11 -Wall
#include <cstdio>
#include <iostream>
#include <iterator> // std::begin など

// 配列の参照で受けるときはこんな感じ。
template <size_t array_size>
void int_array_receiver(int const (&ary)[array_size]) {
  for (auto i : ary) {
    std::cout << i << " ";
  }
  std::cout << std::endl;
}

// C言語風に受けるときはこんな感じ。
void c_style(long const *p, size_t size) {
  for (size_t i = 0; i < size; ++i) {
    std::cout << p[i] << " ";
  }
  std::cout << std::endl;
}

// STL風に受けるときはこんな感じ。
void stl_style(char const *begin, char const * end) {
  for( auto p = begin ; p != end ; ++p ){
    std::cout << 0+*p << " ";
  }
  std::cout << std::endl;
}

void func() {
  // 宣言と定義
  int three_integers[] = {1, 2, 3}; // 要素数の指定は省略できる
  long five_longs[5] = {9, 8, 7};   // { 9, 8, 7, 0, 0 } と同じ。
  char hoge[] = "hoge"; // 文字列リテラルで初期化できる。null-terminator
                        // を含むので hogeは5要素
  char fuga[7] = "fuga"; // 末尾の3つはゼロになる。

  // 関数に渡す

  // 配列の参照を渡すとサイズも渡せる
  int_array_receiver(three_integers);

  // C言語風に渡すときはサイズを計算する必要がある
  c_style(five_longs, sizeof(five_longs) / sizeof(*five_longs));

  // STL風に渡すときは std::begin などを使う
  stl_style( std::begin(hoge), std::end(hoge) );

  // range based for が使える
  for (auto c : fuga) {
    std::cout << c + 0 << " ";
  }
  std::cout << std::endl;
}

int main() { func(); }
```

C++11 から、`std::begin` とか range based for が使えるようになって便利になった。
とはいえ、すぐにポインタになってしまうのは C言語からの伝統で変わっていない。

```C++:C++11
int foo[] = {1,2,3};
auto bar = foo;
```

のようにすると、`bar` はポインタになってしまい、配列の中身はコピーされない。

また、サイズ0の配列を作ろうとするとコンパイルエラーになる。

# std::array

C++11 で導入されたテンプレートクラス。C++11 以降が使えれば、生の配列よりもこちらを使うのがおすすめ。
使い方はこんな感じ：

```C++:C++11
// clang++ -std=c++11 -Wall
#include <array>
#include <cstdio>
#include <iostream>
#include <numeric>

template <typename ary_type> void ref_receive(ary_type const &ary) {
  for (auto i : ary) {
    std::cout << i << " ";
  }
  std::cout << std::endl;
}

void copy_receive(std::array<int, 3> ary) {
  for (auto &i : ary) {
    i += 10;
  }
  ref_receive(ary);
}

std::array<int, 4> returns_ary() { return {9, 8, 7, 6}; }

void func() {
  // 宣言と定義
  std::array<int, 100> garbage; // 初期化子リストがないと初期化されない
  std::array<int, 3> three_integers = {1, 2, 3}; // 要素数の指定は省略できない
  std::array<long, 5> five_longs = {9, 8, 7}; // { 9, 8, 7, 0, 0 } と同じ。
  std::array<char, 5> hoge = {"hoge"}; // 文字列リテラルで初期化できる
  std::array<char, 7> fuga = {"fuga"}; // 末尾の3つはゼロになる。

  std::cout << std::accumulate(garbage.begin(), garbage.end(), 0) // ゴミが出る
            << std::endl;

  // 複製
  auto foo = three_integers; // コンストラクタで複製できる
  decltype(three_integers) bar;
  bar = foo; // 代入演算子でコピーできる

  // 非参照で渡せばコピーされる( 関数内から bar を変更できない )
  copy_receive(bar);

  // 参照で渡せばコピーされない
  ref_receive(bar);

  // 関数の返戻値としても使える
  auto baz = returns_ary();
  ref_receive(baz);
}

int main() { func(); }
```

生配列がすぐに要素へのポインタに縮退してしまうのに対して、`std::array` は普通の値のように振る舞うところが素晴らしい。
関数に渡すことも、関数から返してもらうこともできる。
STLの一員なので、`begin()`, `end()`, `size()` などのメンバ関数が一通り揃っている。

生配列をリプレイスするものなので、動的な感じは全然ない。要素型はもちろん、サイズもコンパイル時に確定する。

生配列と異なり、要素数をゼロにしてもエラーにならない。

ただ。
要素数の省略ができない。生配列ならできるのに。残念。

# std::unique_ptr\<T[]\>

unique_ptr じゃなくてもいいんだけど、例として unique_ptr で。

```c++:c++14
// clang++ -std=c++14 -Wall
#include <iostream>
#include <memory> // std::unique_ptr

std::unique_ptr<char[]> returns_ptr() {
  auto p = std::make_unique<char[]>(10);
  for (size_t ix = 0; ix < 10; ++ix) {
    p[ix] = ix + 1 == 10 ? 0 : 'a' + ix;
  }
  return p;
}

void func(size_t n) {
  // make_unique は C++14以降
  // n は要素数。
  // make_unique だとデフォルトコンストラクタが呼ばれる( int なら 0 になる)
  auto p0 = std::make_unique<int[]>(n);

  auto q = std::make_unique<int[]>(0); // サイズ0でもOK

  // unique_ptr + new なら初期値を入れられる。要素数は省略不能。
  auto p1 = std::unique_ptr<int[]>(new int[3]{11, 22, 33});

  // char の配列を文字列で初期化。clang++ は OK だけど、g++-9 はエラー。
  auto p2 = std::unique_ptr<char[]>(new char[5]{"hoge"});
  std::cout << p2.get() << "\n";

  // unique_ptr + new なら初期化を回避することもできる。
  auto p3 = std::unique_ptr<int[]>(new int[3]);
  for (size_t ix = 0; ix < 3; ++ix) { // range based for は使えない
    std::cout << p3[ix] << " ";       // 不定の値が出力される
  }
  std::cout << std::endl;

  auto r = std::move(p0);             // move できる
  for (size_t ix = 0; ix < n; ++ix) { // range based for は使えない
    std::cout << r[ix] << " ";
  }
  std::cout << std::endl;

  auto s = returns_ptr(); // 返戻値として使える
  std::cout << s.get() << std::endl;
}

int main() { func(3); }
```

ヒープにメモリ確保をする方法としては最も軽量だけど、できることが少ない。
特に、確保されている要素数を知る方法がないのが不便。
move ができるのでわりと使いやすい。

~~「`int` を動的にn個確保したいけど 0で初期化する必要がない」とかいうときには `malloc` するのがいいのかなぁ。~~

`std::unique_ptr<int[]>(new int[3]);` の様にすることで、初期化を回避することができる。
C++20 からは `std::std::make_unique_default_init` が使えるようだが、手元の clang++(Apple LLVM version 10.0.1 (clang-1001.0.46.4)), g++(9.1.0) で `std=c++2a` にしても使えなかった。

上記のコードに書いたとおり、

```C++
new char[5]{"hoge"}
```

と書くと、clang++ はOKで、g++-9 はエラーにする。
どちらが正しいのかはまだ調べていない。謎。

# vector

コードは省略。

だいたい何でもできる。

ただ、
`std::vector<char> hoge = "hoge";`
とか
`std::vector<char> hoge{"hoge"};`
は出来ない。残念。

中身は

* 先頭へのポインタ
* 有効な要素の末尾の次へのポインタ
* 確保されているメモリの末尾の次へのポインタ

の三点セットだと思うので、`std::uniq_ptr<T[]>` よりはかさばる。大差ないけど。

あと。私の知る限り、`int` 等の 0 初期化を回避することはできない。

C++11 になってからは move できるので返戻値として抵抗なく使えるようになった。

# まとめ

||配列|`array`|`unique_ptr<T[]>`|`vector`|
|:--:|:--:|:--:|:--:|:--:|
|要素数の省略|✅|❌|❌|✅|
|初期値の指定|✅|✅|✅※3|✅|
|文字列リテラルで初期化|✅|✅|**?** ※4|❌|
|サイズゼロ|❌|✅|✅|✅|
|代入演算子で複製|❌|✅|❌|✅|
|サイズの指定|静的|静的|動的|動的|
|サイズ変更|❌|❌|❌|✅|
|range based for|✅|✅|❌|✅|
|サイズの取得|計算※1|`o.size()`|できない|`o.size()`|
|利用するメモリ|スタックとか|スタックとか|ヒープ※2|ヒープ※2|
|move|❌|❌※5|✅|✅|
|返戻値としての利用|❌|✅|✅|✅|
|0初期化|回避可能|回避可能|回避可能※3|不可避|

※1: C++17 からは `std::size(o)` が使える
※2: 頑張ればスタックにも作れると思う
※3: `make_unique` ではなく、`unique_ptr<型[]>(new 略);` とする必要がある。
※4: g++-9 と clang++ で意見が違う
※5: コメントいただいたとおり、出来なくはないけど意味があまりない。

上表のとおり、できることと出来ないことがいろいろある。
何を使うのが良いかはケースバイケース。