🧩

C++クイズ:std::vectorの初期化あれこれ

2020/09/22に公開

C++標準ライブラリのなかで最も利用頻度が高いであろう可変長配列std::vectorクラステンプレートは、その構築手段として多様なコンストラクタをオーバーロード提供しています。

さまざまなstd::vector変数の初期化から、どんな可変長配列が生成されるか考えてみてください。もちろんC++標準ライブラリのリファレンスを参照しながらで構いませんよ!

レギュレーション: 本記事ではC++11以降〜最新のC++17言語までを前提とします。もうC++98/03しか知らないということはありませんよね?

出題編

C++11/14レベル(初級)

まずはstd::vector<int>初期化を行う4問の出題です。ヒント:コンストラクタに与えている引数の個数、括弧の種類違い((){})に留意してください。

std::vector<int> v1(2);
std::vector<int> v2{2};
std::vector<int> v3(3, 4);
std::vector<int> v4{3, 4};

C++17レベル(中級)

C++17ではクラステンプレートのテンプレート引数推論が導入され、std::vectorへのテンプレート引数明示を省略可能となりました。ここではstd::vector<int>へと推論される追加4問の出題です。ヒント:必ずしもコンパイルできるわけではありません。

std::vector v5(2);
std::vector v6{2};
std::vector v7(3, 4);
std::vector v8{3, 4};

C++17レベル(上級)

まだまだ物足りないエキスパート向けの上級編として、追加の2問を用意しておきました。ヒント:型推論に注意!

std::vector v0{ 1, 2, 3, 4 };

std::vector v9 ( v0.begin(), v0.end() );
std::vector v10{ v0.begin(), v0.end() };

回答・解説編

C++11/14レベル(初級)

さっそく答え合わせとして、各行のコメントにて初期化結果を記載します。

std::vector<int> v1(2);     // [0,0]
std::vector<int> v2{2};     // [2]
std::vector<int> v3(3, 4);  // [4,4,4]
std::vector<int> v4{3, 4};  // [3,4]
  • v1:コンストラクタに要素数をとるvector(size_type n)が呼び出されます。各要素はデフォルト初期化されたint型の値、つまり 要素0 × 2個 で初期化されます。
  • v2リスト初期化となるためvector(std::initalier_list<int> il)が呼び出されます。つまり 要素2 のみで初期化されます。
  • v3:コンストラクタに要素数と初期値をとるvector(size_type n, const int& value)が呼び出されます。各要素は 初期値4 として 3個 の要素で初期化されます。
  • v4v2同様にリスト初期化となるためvector(std::initalier_list<int> il)が呼び出されます。つまり 要素34 にて初期化されます。

C++17レベル(中級)

先レベルと同様に、各行のコメントにて初期化結果を記載します。

std::vector v5(2);     // コンパイルエラー
std::vector v6{2};     // [2]
std::vector v7(3, 4);  // [4,4,4]
std::vector v8{3, 4};  // [3,4]
  • v5int型の引数2から候補コンストラクタを探しますが、std::vector<T>のテンプレート引数型Tを推論可能な候補は存在しません。よってコンパイルエラーとなります。
    • 引数を1個とる候補コンストラクタvector(const Allocator&), vector(size_type n)は、実引数2から要素型Tを推論できません。
    • 引数を1個とる別の候補コンストラクタvector(const vector<T>&)(コピーコンストラクタ), vector(vector<T>&&)(ムーブコンストラクタ)は要素型T情報を含むものの、int型の実引数2には適合しません。
  • v6, v7, v8:先レベルのv2, v3, v4と同じコンストラクタが選択されます。特に面白いことは何もありません。

C++17レベル(上級)

最後の答え合わせです。各行のコメントにて初期化結果を記載します。

std::vector v0{ 1, 2, 3, 4 };

std::vector v9 ( v0.begin(), v0.end() );  // [1,2,3,4]
std::vector v10{ v0.begin(), v0.end() };  // [v0.begin(),v0.end()]
  • v9std::vector<int>::iterator型の実引数v0.begin(), v0.end()から、テンプレートコンストラクタtemplate <class InputIterator> vector(InputIterator first, InputIterator last)が選択されます。
    • 推論ガイドtemplate<class InputIterator> vector(InputIterator, InputIterator) -> vector<typename iterator_traits<InputIterator>::value_type>より、テンプレートパラメータTtypename iterator_traits<std::vector<int>::iterator>::value_type、つまり要素型intが推論されます。
  • v10リスト初期化となるためvector(std::initalier_list<std::vector<int>::iterator> il)が優先されます。結果として要素型std::vector<int>::iteratorへと推論され、イテレータを要素にもつ可変長配列として初期化されます。

v10の詳細解説は「シーケンスコンテナ×イテレータ・ペア×リスト初期化×推論ガイド=?」に譲ります。

おわりに

いやぁ、C++の初期化って本当に奥が深いもんですね〜

Discussion