constexpr で テンプレートメタプログラミング

この記事はC++ AdventCalendar2014 6日目の記事になります


C++14!!祝C++14!!祝C++14!!祝C++14!!

C++14においてconstexprの大幅な規制緩和が制定された
これによりconstexprにおいてforなどループ文の記述が可能になった

一方そのころTMPは

残念ながらテンプレート用for文が入ることはなかった
しかしTMPはfor文を必要としている。forは力だ。力はいい。持ちすぎて困ることはない

そしてTMPにかなり近いところにいるconstexprは今、for文を持っている。これを利用しない手はない

constexprを用いたフィルターメタ関数の実装
template<class tList,template<class>class Pred>
struct filter
{
    static const std::size_t N=tList::size;
    template<std::size_t...Idxs>
    static constexpr sprout::pair<std::size_t,sprout::array<std::size_t ,N>> impl(std::index_sequence<Idxs...>)
    {
        sprout::array<bool ,N> v{{(Pred<at<tList,Idxs>>{}())...}};
        sprout::array<std::size_t ,N>idx{{Idxs...}};
	std::size_t k=0;
	for(std::size_t i=0;i<N;++i)
	{
		if(v[idx[i]])
		{
			idx[k]=idx[i];
			++k;
		}
	}
         return {k,idx};
    }
    static constexpr auto res=impl(std::make_index_sequence<N>{});
    template<std::size_t...Idxs>
    static constexpr List<at<tList,res.second[Idxs]>...> impl2(std::index_sequence<Idxs...>){return {};}

    using type=decltype(impl2(std::make_index_sequence<res.first>{}));
};

[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ

Impl は必要な型のインデックス配列とその長さを返す
impl2は結果の長さと同じ長さのindex_sequence→残す型のインデックス→残す型に変換していきタイプリストにする

結果、タイプリストと評価用メタ関数を受け取り、タイプリストを返すメタ関数を作ることができた


せっかくだからバブルソートも書いてみた
[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ
比較するときにis_base_ofを使うような場合は使えないが実用上問題はないだろう*1

型安全printfなど構文解析が必要な所作もconstexprを用いて実装可能だ*2

このようにconstexprを使い頭皮に優しい、もしかしたらコンパイラにも優しいTMPが可能になる

まとめ

このように今まで型を必要としていた分野、今までゴリゴリとTMPしていた分野でもconstexprを用いることができる。やるやらない出来る出来ないは別としてゴリゴリと型を用いるプログラミングをする際は頭の片隅に入れておくべきである

なおこれらのコードはClangでしか動かない
コンパイラ、とくにMS社の蒼いコンパイラには頑張ってもらいたい


C++ Advent Calendar 2014 - Qiita

明日はh_hiro_ さんです
よろしくお願いします

おまけ。

値と型、そして未来

2014年現在 型安全printfは実用上マクロ(SPROUT_TYPES_STRING_TYPEDEFなど)に頼る必要がある

printf(MACRO("%d%d%d"),a,b,c)

値を型に用意に変換する構文がないのだ。マクロを使わない場合ラムダ式内で構造体を作って云々

[]{struct hoge{constexpr auto operator()(){return "%d%d%d";}};return hoge{};}()

[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ

もしくは

mpl_string<'%','d','%','d','%','d'>{}

非常に残念な感じだ


残念な感じなので未来に願いを託そう
まず文字列ユーザー定義リテラル
すごいユーザ定義リテラルたのしく遊ぼう - ボレロ村上 - ENiyGmaA Code
の一番下に載っているヤツだ

これはmpl_stringの構築を容易にする構文だ
mpl_stringからconstexprな文字列に変換するのは容易なのでコレがあれば型printfつらい問題は解決する


もう一つの可能性はラムダ式だ。ラムダ式がデフォルトコンストラクタを持ち、constexprになれば、つまり

[]{return "%d%d%d";}

struct _
{
	constexpr auto operator()()const{return "%d%d%d";}
}

を生成するようになれば,ラムダ式はintegral_constant的な関数とみなせるようになる
[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ
なぜラムダ式がconstexprではないかというと
本の虫: もしlambda式がconstexprだったら

という経緯があるためである
逆にいうとコンセプトが入った段階でラムダ式がconstexpr化する可能性も無きにしも非ず、かもしれない

*1:その場合はバイトニックソートとか使おう

*2:面倒なので実装しないが