constな変数の初期化とラムダ

この前、少し初期化が面倒な数値のリストを作らなければならなくなった。しかも結構急いでいた。

適当な例として以下のようなものを考える。

const std::vector<std::size_t> indices{1,4,7,10,13, ... 298}; //流石に手では書けない

こんなもの、for文を使えばいいのでは?

std::vector<std::size_t> indices;
for(std::size_t i=0; i<100; ++i)
    indices.push_back(i*3+1);

上のコードは完璧ではない(ex. reserveするべきだ)が、とりあえず動きはする。

しかしその後少し面倒なことをするので、できれば私はこれをconstにしたかった。不注意で書き換えてしまうのが怖かった。

外部でCSVを作って、

const std::vector<std::size_t> indices{
#include "indices.csv"
};

とするのも一瞬考えたが、流石に却下した。

例えばBoost Iteratorを使えば、これは普通にIteratorを2つ渡してその範囲を使って初期化するコンストラクタが使える。

const auto f = [](std::size_t i){return i*3+1;};
const std::vector<std::size_t> indices(
    boost::make_transform_iterator(
        boost::make_counting_iterator<std::size_t>(1), f),
    boost::make_transform_iterator(
        boost::make_counting_iterator<std::size_t>(100), f)
    );

だが長い。正しいやり方だが、その時私は本当に急いでいたので(実際、マクロ定義をして一部のコードを生成した程だ)、これはちょっと許容できなかった。

そして、関数の返却値としてならconstで受けられる、と思ったあと、一瞬関数を作ろうとして、すぐに一時的なその場で使える関数の存在を思い出した。

lambdaだ。

const std::vector<std::size_t> indices = [](){ // ラムダ定義開始
    std::vector<std::size_t> v;
    for(std::size_t i=0; i<100; ++i)
        v.push_back(i*3+1);
    return v;
}(); // lambda定義を終了し "}"、即座に呼び出す "()"。
// その場でラムダの中身が実行され、値が返る。それが`indices`になる。

その場でlambdaを作って、その中で必要なものを生成し、その場でconstなものに代入した。

今までstd::algorithmとの組み合わせとか、関数を渡すというような状況でばかり使ってきたが、lambdaにはこんな使い方もある。綺麗な書き方かどうかはおいておいて。