コンテンツにスキップ

C++/標準ライブラリ/initializer list

出典: フリー教科書『ウィキブックス(Wikibooks)』

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クラスと関連する非メンバ関数によってこの機能が実現されています。一方で、実行時のオーバーヘッドが無視できないため、パフォーマンスが重要な場合には注意が必要です。初期化リストの利用を検討する際は、メリット・デメリットを十分に理解しましょう。