C++教科書/標準ライブラリ編/initializer list

編集

はじめに

編集

C++11から導入された初期化リスト(initializer list)は、コンテナや配列、ユーザー定義型の初期化を簡潔に記述できる新しい機能です。この機能はstd::initializer_listクラステンプレートと関連する関数によって実装されています。本章ではstd::initializer_listの使い方と内部動作を詳しく解説します。

initializerリストの基本

編集

initializerリストとは

編集

initializerリストとは、波カッコ{}で囲まれた初期化子のリストのことです。以下は配列の初期化例です。

int arr[] = {1, 2, 3, 4, 5};

これまでは初期化子リストを使っていましたが、initializerリストではその初期化子リストを一時的なstd::initializer_listオブジェクトとして扱えるようになりました。

initializerリストの使用例

編集

std::vectorなどのコンテナクラスには、initializerリストを直接渡して初期化できるコンストラクターが用意されています。

std::vector<int> v = {1, 2, 3, 4, 5}; // std::vector<int>のコンストラクターにinitializerリストを渡す

構造体やクラスのコンストラクター呼び出しでも使えます。

struct Point { int x, y; };
Point p = {1, 2}; // Pointのコンストラクターに{1, 2}を渡す

コンストラクターでのinitializer_listの活用

編集

initializerリストを引数に取るコンストラクターを定義することで、convenientな初期化が可能になります。

class IntVector {
  public:
    IntVector(std::initializer_list<int> init) : v{init} {}
  private:
    std::vector<int> v;
};

IntVector iv = {1, 2, 3, 4, 5}; // IntVectorのコンストラクターに{1, 2, 3, 4, 5}を渡す

initializer_listクラスの詳細

編集

initializer_listクラステンプレートの定義

編集
template <class E>
class initializer_list {
  public:
    using value_type = E;
    using reference = const E&;
    using const_reference = const E&;
    using size_type = size_t;

    using iterator = const E*;
    using const_iterator = const E*;

    constexpr initializer_list() noexcept;

    constexpr size_t size() const noexcept;
    constexpr const E* begin() const noexcept;
    constexpr const E* end() const noexcept;
};

initializer_listはテンプレートクラスで、要素型Eをテンプレート引数に取ります。メンバ型の定義から、このクラスはconstの配列を表すラッパーだと分かります。

メンバ型の説明

編集
value_type, reference, const_reference
要素型EとEへの参照型
size_type
要素数を表すサイズ型(size_t)
iterator, const_iterator
要素へのconstポインター

メンバ関数の説明

編集
size()
要素数を返す
begin()
先頭要素を指すイテレーターを返す
end()
終端を指すイテレーターを返す

initializer_listへのアクセス

編集

begin, end非メンバ関数

編集

begin, end関数は標準ライブラリに特化された非メンバ関数版が用意されています。

template<class E>
const E* begin(initializer_list<E> il) noexcept;

template<class E>
const E* end(initializer_list<E> il) noexcept;

これにより、ranged-forやアルゴリズムにinitializer_listを渡せます。

for (int x : {1,2,3,4,5}) { /* ... */ }
std::cout << std::accumulate(std::begin({1,2,3}), std::end({1,2,3}), 0); // 出力:6

initializer_listの応用

編集

コンテナ初期化での利用

編集

std::vectorなどの多くのコンテナクラスには、initializer_listを引数に取るコンストラクター・代入演算子が用意されています。

std::vector<int> v{1, 2, 3, 4, 5};  // コンストラクター
v = {6, 7, 8}; // 代入演算子

ユーザー定義型での利用

編集

ユーザー定義型のコンストラクターにinitializer_listを受け取ることで、簡潔な初期化構文が可能になります。

class Point {
    int x, y;
  public:
    Point(std::initializer_list<int> list) {
        auto it = list.begin();
        x = *it++;
        y = *it;
    }
};

Point p = {1, 2}; // Pointのコンストラクターに{1, 2}を渡す

initializer_listを引数に取る関数

編集

initializer_listを関数の引数に取ることもできます。これは可変長引数の代替として使えます。

void print(std::initializer_list<int> list) {
    for (int x : list) {
       std::cout << x << " ";
    }
    std::cout << endl;
}

print({1,2,3,4,5}); // 出力:1 2 3 4 5

まとめ

編集

initializerリストはコンテナやユーザー定義型の初期化を簡潔に記述できる強力な機能です。std::initializer_listクラスと関連する非メンバ関数によってこの機能が実現されています。一方で、実行時のオーバーヘッドが無視できないため、パフォーマンスが重要な場合には注意が必要です。初期化リストの利用を検討する際は、メリット・デメリットを十分に理解しましょう。