variadic templateを関数の引数にすると最後までマッチしてしまうので関数の先頭に任意個の引数を取るような関数を書けない。困りますよね。
可変個の引数を先に渡して、最後に追加で何かを渡したい、そんなこともあると思います。
template<typename ... Ts, typename F> void f(Ts&& ... args, F functor) { (functor(std::forward<Ts>(args))...); return ; }
本日ご紹介するのはこちら!
両サイドから考えていきましょう。tuple一個とfunctor一個を取る関数は書けます。
template<typename F, typename ... Ts> void f_impl(std::tuple<const Ts&...> t, F functor) { // ... }
また、単なるvariadic templateは何にでもマッチするので、自明にこういう関数は書けます。
template<typename ... Ts> void f(const Ts& ... args) { // ... }
この2つの間を埋めていきましょう。最後の一個の引数になるまで、先頭の引数をtupleにまとめていくような関数を間に挟めばいいわけですね。
最後の一個になったらf_impl
に飛ばせばOKです。
template<typename Tuple, typename F> void f_aux(Tuple t, const F& functor) { return f_impl(t, functor); } template<typename Tuple, typename T, typename ... Ts> std::enable_if_t<sizeof...(Ts) != 0> f_aux(Tuple t, const T& head, const Ts& ... tail) { return f_aux(push_back(t, head), tail...); }
std::tuple
を結合できるようにしないといけません。
template<typename Tuple, typename Elem, std::size_t ... Is> std::tuple<typename std::tuple_element<Is, Tuple>::type ..., const Elem&> push_back(Tuple tpl, const Elem& item, std::index_sequence<Is...>) { return std::make_tuple(std::cref(std::get<Is>(tpl))..., std::cref(item)); } template<typename Tuple, typename T, typename ... Ts> std::enable_if_t<sizeof...(Ts) != 0> f_aux(Tuple t, const T& head, const Ts& ... tail) { constexpr std::size_t N = std::tuple_size<Tuple>::value; return f_aux(push_back(t, v, std::make_index_sequence<N>{}), args...); }
準備が整ったので、f
からf_aux
に転送しましょう。
template<typename T, typename ... Ts> void f(const T& head, const Ts& ... args) { return func_aux(std::make_tuple(std::cref(head)), args...); }
出来上がったものがこちらになります。