C++0x で make_fused を書く

Variadic Templates を使うことによって、驚くほど簡潔に書くことができます (Egg に合わせて fuse という名前にしています)。

#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>

namespace fuse_detail {

template <std::size_t ...Indexes>
struct index_tuple
{
    typedef index_tuple<Indexes..., sizeof...(Indexes)> next;
};

template <std::size_t N>
struct build_index_tuple
{
    typedef typename build_index_tuple<N - 1>::type::next type;
};

template <>
struct build_index_tuple<0>
{
    typedef index_tuple<> type;
};

template <class F, class Args, std::size_t ...Indexes>
auto call(F const &f, Args &&args, index_tuple<Indexes...>)
    -> decltype(f(std::get<Indexes>(std::forward<Args>(args))...))
{
    return f(std::get<Indexes>(std::forward<Args>(args))...);
}

}

template <class F>
struct fused
{
    F f;

    template <
        class Args,
        class Indexes = typename fuse_detail::build_index_tuple<
            std::tuple_size<typename std::remove_reference<Args>::type>::value
        >::type
    >
    auto operator()(Args &&args) const
        -> decltype(fuse_detail::call(f, std::forward<Args>(args), Indexes()))
    {
        return fuse_detail::call(f, std::forward<Args>(args), Indexes());
    }
};

template <class F>
inline fused<typename std::decay<F>::type> fuse(F &&f)
{
    return { std::forward<F>(f) };
}

使い方

int unfused_plus(int i, int j, int k)
{
    return i + j + k;
}

int main()
{
    std::cout << fuse(&unfused_plus)(std::forward_as_tuple(1, 2, 3)); // 6
}

タプル*1として渡された引数をいかに関数に forward するかが問題となるわけですが、それには GCC の pair piecewise construction の実装が参考になります。すなわち、引数のタプル Args &&args が与えられた場合、

  1. std::tuple_size によって引数の数 N を求める
  2. Variadic な列 std::size_t ...Indexes = 0, 1, 2, ..., N - 1 を生成する
  3. std::get(std::forward(args))... を目的の関数に渡す

ことによって forward を行うことができます。
Variadic Templates の力がよくわかる例でした。

*1:std::tuple_size と std::get を特殊化すれば、Fusion の列も使えるようになります