apply()を標準アルゴリズムと組み合わせられるように改善する
N3915 apply() call a function with arguments from a tuple (V3)
現在、C++14後のLibrary Fundamentals TSに提案されているapply()
関数。これは、タプルを引数として関数を呼び出すというユーティリティ関数ですが、機能が小さすぎるので実用に耐えません。
この関数は、実際には複数のRangeをzipしてアルゴリズムに渡すために使用します。そのため、標準アルゴリズムと組み合わせて使えなくてはなりません。標準アルゴリズムと組み合わせられるようにするには、「複数引数を受け取る関数を、タプルを引数として実行する形に変換する」という機能とその引数適用を、2段階に分けて行える必要があります。
vector<tuple<T1, T2, T2>> v; for_each(v.begin(), v.end(), make_apply([](T1 a, T2 b, T3 c) { cout << a << ' ' << b << ' ' << c << endl; }));
これと同等の機能は、Boost.Fusionにだいぶ前からmake_fused()
という関数で提供されています。標準にこの機能を導入するのであれば、Boost.Fusionの経験を反映させたものにするべきだと思います。
2段階化の実装は、apply()
関数のラッパーとして実装できます。
#include <tuple> #include <utility> template<typename F, typename Tuple, size_t... I> auto apply_impl(F&& f, Tuple&& args, std::index_sequence<I...>) { return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(args))...); } template<typename F, typename Tuple, typename Indices = std::make_index_sequence<std::tuple_size<Tuple>::value>> auto apply(F&& f, Tuple&& args) { return apply_impl(std::forward<F>(f), std::forward<Tuple>(args), Indices()); } template<typename F, typename Tuple, size_t... I> auto apply_impl(F&& f, const Tuple& args, std::index_sequence<I...>) { return std::forward<F>(f)(std::get<I>(args)...); } template<typename F, typename Tuple, typename Indices = std::make_index_sequence<std::tuple_size<Tuple>::value>> auto apply(F&& f, const Tuple& args) { return apply_impl(std::forward<F>(f), args, Indices()); } template <class F> class apply_functor { F f_; public: explicit apply_functor(F&& f) : f_(std::forward<F>(f)) {} template <class Tuple> auto operator()(Tuple&& args) { return apply(std::forward<F>(f_), std::forward<Tuple>(args)); } template <class Tuple> auto operator()(const Tuple& args) { return apply(std::forward<F>(f_), args); } }; template <class F> apply_functor<F> make_apply(F&& f) { return apply_functor<F>(std::forward<F>(f)); }
サンプル:
#include <iostream> #include <vector> #include <string> #include <algorithm> int main() { std::vector<std::tuple<int, char, std::string>> v = { {1, 'a', "Alice"}, {2, 'b', "Bob"}, {3, 'c', "Carol"} }; std::for_each(v.begin(), v.end(), make_apply([](int a, char b, const std::string& c) { std::cout << a << ' ' << b << ' ' << c << std::endl; } )); }
出力:
1 a Alice 2 b Bob 3 c Carol
という意見を出そうかと思っています。ようやく実装コードを書く時間がとれたので、もうちょっと改善できるところがないかを検討してから出します。