将函数应用于元组的每个元素

时间:2017-11-07 09:11:54

标签: c++ tuples c++14

给定一个类似std::tuple的对象(即定义的tuple_sizeget语义)和一元仿函数对象ftor,我希望能够调用{{ 1}}在ftor - 类似对象的每个元素上。

如果我忽略返回值,我知道int数组技巧:

tuple

如果我想要返回值,我可以通过调用namespace details { template <typename Ftor, typename Tuple, size_t... Is> void apply_unary(Ftor&& ftor, Tuple&& tuple, std::index_sequence<Is...>) { using std::get; int arr[] = { (ftor(get<Is>(std::forward<Tuple>(tuple))), void(), 0)... }; } } // 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<Tuple>::value> {}); } 替换int []技巧,然后返回。如果对std::make_tuple对象的调用都没有ftor返回值,那就可以了。

我的问题是:考虑到我想得到调用的结果,我该如何处理可能返回void的调用?

唯一的要求是我应该将结果作为元组得出,并且能够告诉哪个调用导致所述结果元组中的哪个元素。

2 个答案:

答案 0 :(得分:4)

正如@ Jarod42所建议的那样,用一个额外的图层来处理调用,这个图层负责用虚拟结构替换void返回,这样做可以解决问题:

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> {});
}

现场演示可在Coliru

上找到

我使用SFINAE来区分两个重载。它看起来有点难看,所以如果你有任何改进建议......我都是耳朵!

答案 1 :(得分:2)

另一种方式:

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> {});
}

在上文中,它将operator,应用于ftorapply_unary_helper_t的结果。如果ftor的结果为void,则rstd::initializer_list<details::apply_unary_helper_t>,否则rstd::initializer_list<decltype(ftor(...))>,您可以使用。{/ p>