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