Applying a function to each element of a tuple

笑着哭i 提交于 2019-12-04 02:38:47

Another way:

namespace details {

struct apply_unary_helper_t {};

template<class T> 
T&& operator,(T&& t, apply_unary_helper_t) { // Keep the non-void result.
    return std::forward<T>(t); 
}

template <typename Ftor, typename Tuple, size_t... Is>
void apply_unary(Ftor&& ftor, Tuple&& tuple, std::index_sequence<Is...>) {
    auto r = {(ftor(std::get<Is>(std::forward<Tuple>(tuple))), apply_unary_helper_t{})...};
    static_cast<void>(r); // Suppress unused variable warning.
}

} // namespace details

template <typename Ftor, typename Tuple>
void apply_unary(Ftor&& ftor, Tuple&& tuple) {
    details::apply_unary(std::forward<Ftor>(ftor),
                         std::forward<Tuple>(tuple),
                         std::make_index_sequence<std::tuple_size<std::remove_reference_t<Tuple>>::value> {});
}

In the above, it applies operator, to the result of ftor and apply_unary_helper_t. If the result of ftor is void, then r is std::initializer_list<details::apply_unary_helper_t>, otherwise r is std::initializer_list<decltype(ftor(...))> which you can make use of.

As suggested by @Jarod42, wrapping the call with an additional layer that takes care of replacing void return with dummy struct will do the trick:

struct no_return {};

namespace details {

template <typename Ftor, typename Arg>
auto call(Ftor&& ftor, Arg&& arg)
    -> std::enable_if_t<std::is_void<decltype(std::forward<Ftor>(ftor)(std::forward<Arg>(arg)))>::value, no_return> {
    std::forward<Ftor>(ftor)(std::forward<Arg>(arg));
    return no_return {};
}

template <typename Ftor, typename Arg>
auto call(Ftor&& ftor, Arg&& arg)
    -> std::enable_if_t<!std::is_void<decltype(std::forward<Ftor>(ftor)(std::forward<Arg>(arg)))>::value, decltype(std::forward<Ftor>(ftor)(std::forward<Arg>(arg)))> {
    return std::forward<Ftor>(ftor)(std::forward<Arg>(arg));
}

template <typename Ftor, typename Tuple, size_t... Is>
auto apply_unary(Ftor&& ftor, Tuple&& tuple, std::index_sequence<Is...>) {
    using std::get;
    return std::tuple<decltype(call(ftor, get<Is>(std::forward<Tuple>(tuple))))...> { call(ftor, get<Is>(std::forward<Tuple>(tuple)))... } ;
}

} // namespace details

template <typename Ftor, typename Tuple>
auto apply_unary(Ftor&& ftor, Tuple&& tuple) {
    return details::apply_unary(std::forward<Ftor>(ftor),
                                std::forward<Tuple>(tuple),
                                std::make_index_sequence<std::tuple_size<std::decay_t<Tuple> >::value> {});
}

A live demo is available on Coliru

I did this using SFINAE to distinguish between the two overloads. It looks kinda ugly so if you have any improvement suggestion... I'm all ears!

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!