Rokiのチラ裏

学生による学習のログ

TMP におけるバインドとその利用

私の観測範囲内ではあまり有名で無いようなので発信。バインドとは C++ において Callable を満たすものに対し引数の束縛を行い、束縛済みの新たな Callable オブジェクトを生成する事を広義に言うが、これを TMP で行うにはどうすれば良いか。つまり例えば次のようにするにはどうすれば良いか。

typedef bind<std::is_same, int> type;
type::type<int>::value; // == std::is_same<int, int>::value

C++98 から次期標準規格である C++20 の間で、テンプレートテンプレートパラメータを typedef することはできない。

template <template <class...> class F, class... Ts>
struct bind { typedef ??? type; };

しかしこれは意外と単純で、次のようにすれば書ける。

template <template <class...> class F, class... Ts>
struct bind {
    template <class... Us>
    struct type : std::bool_constant<F<Ts..., Us...>::value> {};
};

TMP とは純粋関数型プログラミングであるので、バインドのような機能は役立つ場面が多い。例えば、与えられたポリシーに従う型でグループ化するといった処理には、次のようにして利用するととても実装が楽だ。

これは次のように動作する。

using srook::tmpl::vt::packer;
struct X {};
struct Y { Y(const X&); };

static_assert(
    std::is_same<
        srook::tmpl::vt::group_by_t<std::is_convertible, packer<int, int, X, Y>>,
        packer<packer<int, int>, packer<X, Y>>
    >::value
);

この実装で利用されている take_whileD と drop_whileD は、バインドされたメタ関数を受け付けるメタ関数であるが、基本的には haskell のそれと同等である。尚、この group_by も haskell の それと同等である。さらに、ソート等の処理を記述する際には、指定された順序の逆順を表せる機能があると実装が楽であるがこれも同様にしてバインド機能で達成できる。以下はその利用によるクイックソートの実装である。

この実装では、placeholder の機能を利用して引数の入力をコントロールしている。これは、C++ 標準ライブラリに属するstd::bindを意識して作られたメタ機能であり、次の通り同じようにして動作する。

#include <type_traits>
#include <srook/tmpl/vt/bind.hpp>

template <class T1, class T2, class T3>
struct F : std::conjunction<std::is_same<T1, double>, std::is_same<T2, int>, std::is_same<T3, char>> {};

typedef srook::tmpl::vt::bind<F, srook::tmpl::vt::placeholders::_1, int, srook::tmpl::vt::placeholders::_2> bind_type1;
static_assert(bind_type1::type<double, char>::value);

typedef srook::tmpl::vt::bind<F, srook::tmpl::vt::placeholders::_2, int, srook::tmpl::vt::placeholders::_1> bind_type2;
static_assert(bind_type2::type<char, double>::value);

その他にも、リポジトリにて Variadic templates に対する高度なリスト処理が可能なメタ関数、高階関数などをいくつか公開している。