Rokiのチラ裏

学生による学習のログ

non-type template parameter のシーケンス生成におけるプレースホルダなど

執筆時現在、C++1z(17)から追加される、non-type template parameter でのautoを使ってゴリゴリとコンパイルを行うと、コンパイル時間が型を指定している場合と比較して圧倒的に伸びる事があるので*1、この機能を使わずに C++14 までの機能だけで任意の型のシーケンス*2を生成できるように、以下のようなものを書いた記録。

github.com

これは、機能的にはstd::make_index_sequenceの上位互換であると言える。std::make_index_sequenceは、std::index_sequenceにシーケンスを溜め込む。つまりstd::size_tの値を持つシーケンスしか生成できない*3。これに対して、non-type template parameter が許容する任意の型を指定する事ができたり、値の適用時にプレースホルダのようなものが使えたりするようにした。

#include<srook/mpl/constant_sequence/algorithm/make_sequence.hpp>

まず、普通のシーケンス生成は以下のようにできる。これは、std::make_index_sequenceと変わらない。

 using type = srook::constant_sequence::make_sequence<10>;

生成される型は以下となる。

std::integer_sequence<unsigned long, 0ul, 1ul, 2ul, 3ul, 4ul, 5ul, 6ul, 7ul, 8ul, 9ul>

これを例えばint型のシーケンスにしたければ、以下のようにする。

using type = srook::constant_sequence::make_sequence<10,srook::constant_sequence::make_sequence_utility::cast<int>>;

この時生成される型は以下となる。

std::integer_sequence<int, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9>

また、同じ値で埋めたくなる事もあるだろう。以下のようにする。

using type = srook::constant_sequence::make_sequence<10,srook::constant_sequence::make_sequence_utility::initialize<int,42>>;

生成される型は以下となる。

std::integer_sequence<int, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42>

任意の操作をしたい事もあるだろう。その場合は、コンパイル時にデフォルトコンストラクトが可能でありかつコンパイル時に呼び出しが可能である operator() を持つクラスを渡す事で適用される*4

struct Plus2char{
        explicit constexpr Plus2char() = default;
        constexpr char operator()(std::size_t x) const noexcept
        {
                return char(x * 2);
        }
};

using type = srook::constant_sequence::make_sequence<10,Plus2char>;

生成される型は以下となる。

std::integer_sequence<char, (char)0, (char)2, (char)4, (char)6, (char)8, (char)10, (char)12, (char)14, (char)16, (char)18>

引数にシーケンスの値が渡ってくる。また、戻り型を変えれば、その値でシーケンスに溜め込むようにしてある。ここまではまぁ普通だと思うのだが、少し実験的な部分が以下の機能。

using namespace srook::constant_sequence::make_sequence_utility;

using type = srook::constant_sequence::make_sequence<10,decltype(std::integral_constant<int,2>() * placeholders::_val)>;

生成される型は以下となる。

std::integer_sequence<int, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18>

+-*/ をそれぞれこの機能向けに定義していて、呼び出されるとその演算処理の内容が埋め込まれた型を生成するだけなのだが、少し表現力豊かになったりする。
最後に、シーケンスのリバース機能。

using namespace srook::constant_sequence::make_sequence_utility;

using type = srook::constant_sequence::make_sequence<10,decltype(std::integral_constant<int,2>() * placeholders::_val),Reverse>;

以下のように生成される。

std::integer_sequence<int, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0>

*1:原因究明中

*2:勿論浮動小数点数型などは non-type template parameter に渡せないので不可能

*3:std::make_index_sequence はそもそも”インデックス”のシーケンス生成を目的としているので、その用途としては十分に満たしている。

*4:以前大体同じような事をしているが、今回は auto を使っていない点で若干異なる