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
という意見を出そうかと思っています。ようやく実装コードを書く時間がとれたので、もうちょっと改善できるところがないかを検討してから出します。
参照