【问题标题】:Passing many functions and storing all their results in a tuple传递许多函数并将其所有结果存储在一个元组中
【发布时间】:2016-07-05 09:48:31
【问题描述】:

考虑这个输出:

int foo (int, char) {std::cout << "foo\n";  return 0;}
double bar (bool, double, long ) {std::cout << "bar\n";  return 3.5;}
bool baz (char, short, float) {std::cout << "baz\n";  return true;}

int main() {
    const auto tuple = std::make_tuple(5, 'a', true, 3.5, 1000, 't', 2, 5.8);
    multiFunction<2,3,3> (tuple, foo, bar, baz);  // foo  bar  baz
}

所以multiFunction&lt;2,3,3&gt; 获取tuple 的前2 个元素并将它们传递给foo,接下来的3 个tuple 元素并将它们传递给bar,等等......我得到了这个工作(除了当函数有重载时,这是一个单独的问题)。但是每个调用的函数的返回值都会丢失。我希望将这些返回值存储在某个地方,例如

std::tuple<int, double, bool> result = multiFunction<2,3,3> (tuple, foo, bar, baz);

但我不知道如何实现。对于那些想要帮助完成这项工作的人,这是我迄今为止的(更新的)工作代码,它仅将输出存储到字符串流中。取回所有值并不容易,特别是如果保存在流中的对象是复杂的类。

#include <iostream>
#include <tuple>
#include <utility>
#include <sstream>

template <std::size_t N, typename Tuple>
struct TupleHead {
    static auto get (const Tuple& tuple) {  // The subtuple from the first N components of tuple.
        return std::tuple_cat (TupleHead<N-1, Tuple>::get(tuple), std::make_tuple(std::get<N-1>(tuple)));
    }
};

template <typename Tuple>
struct TupleHead<0, Tuple> {
    static auto get (const Tuple&) { return std::tuple<>{}; }
};

template <std::size_t N, typename Tuple>
struct TupleTail {
    static auto get (const Tuple& tuple) {  // The subtuple from the last N components of tuple.
        return std::tuple_cat (std::make_tuple(std::get<std::tuple_size<Tuple>::value - N>(tuple)), TupleTail<N-1, Tuple>::get(tuple));
    }
};

template <typename Tuple>
struct TupleTail<0, Tuple> {
    static auto get (const Tuple&) { return std::tuple<>{}; }
};

template <typename Tuple, typename F, std::size_t... Is>
auto functionOnTupleHelper (const Tuple& tuple, F f, const std::index_sequence<Is...>&) {
    return f(std::get<Is>(tuple)...);
}

template <typename Tuple, typename F>
auto functionOnTuple (const Tuple& tuple, F f) {
    return functionOnTupleHelper (tuple, f, std::make_index_sequence<std::tuple_size<Tuple>::value>{});
}

template <typename Tuple, typename... Functions> struct MultiFunction;

template <typename Tuple, typename F, typename... Fs>
struct MultiFunction<Tuple, F, Fs...> {
    template <std::size_t I, std::size_t... Is>
    static inline auto execute (const Tuple& tuple, std::ostringstream& oss, const std::index_sequence<I, Is...>&, F f, Fs... fs) {
        const auto headTuple = TupleHead<I, Tuple>::get(tuple);
        const auto tailTuple = TupleTail<std::tuple_size<Tuple>::value - I, Tuple>::get(tuple);
    //  functionOnTuple (headTuple, f);  // Always works, though return type is lost.
        oss << std::boolalpha << functionOnTuple (headTuple, f) << '\n';  // What about return types that are void???
        return MultiFunction<std::remove_const_t<decltype(tailTuple)>, Fs...>::execute (tailTuple, oss, std::index_sequence<Is...>{}, fs...);
    }
};

template <>
struct MultiFunction<std::tuple<>> {
    static auto execute (const std::tuple<>&, std::ostringstream& oss, std::index_sequence<>) {  // End of recursion.
        std::cout << std::boolalpha << oss.str();
        // Convert 'oss' into the desired tuple?  But how?
        return std::tuple<int, double, bool>();  // This line is just to make the test compile.
    }
};

template <std::size_t... Is, typename Tuple, typename... Fs>
auto multiFunction (const Tuple& tuple, Fs... fs) {
    std::ostringstream oss;
    return MultiFunction<Tuple, Fs...>::execute (tuple, oss, std::index_sequence<Is...>{}, fs...);
}

// Testing
template <typename T> int foo (int, char) {std::cout << "foo<T>\n";  return 0;}
double bar (bool, double, long ) {std::cout << "bar\n";  return 3.5;}
template <int...> bool baz (char, short, float) {std::cout << "baz<int...>\n";  return true;}

int main() {
    const auto tuple = std::make_tuple(5, 'a', true, 3.5, 1000, 't', 2, 5.8);
    std::tuple<int, double, bool> result = multiFunction<2,3,3> (tuple, foo<bool>, bar, baz<2,5,1>);  // foo<T>  bar  baz<int...>
}

【问题讨论】:

  • 只需更改 functionOnTuple 以返回单个元素 tuple。对于void,返回tuple&lt;&gt;(或者可能是tuple&lt;this_function_returns_void_t&gt;)。然后你可以一起tuple_cat他们。显然这不是最有效的,但你的其余代码也不是。
  • 该问题的解决方案很有趣且内容丰富,但我很难理解“多重呼叫”是最优雅的解决方案的问题。似乎有点……无法维护?
  • @Richard Hodges 只是另一层间接性,有一天可能会派上用场。
  • @T.C.谢谢你的建议。我使用您的想法修改了我的解决方案并将其发布。所有返回值现在都存储在所需的元组中(包括 void)。
  • 您可能对this问题解决方案感兴趣

标签: c++ templates tuples c++14 variadic


【解决方案1】:

这是一种贪婪推断参数数量的方法:

#include <tuple>

namespace detail {
    using namespace std;
    template <size_t, size_t... Is, typename Arg>
    constexpr auto call(index_sequence<Is...>, Arg&&) {return tuple<>{};}

    template <size_t offset, size_t... Is, typename ArgT, typename... Fs>
    constexpr auto call(index_sequence<Is...>, ArgT&&, Fs&&...);

    template <size_t offset, size_t... Is,
              typename ArgT, typename F, typename... Fs,
              typename=decltype(declval<F>()(get<offset+Is>(declval<ArgT>())...))>
    constexpr auto call(index_sequence<Is...>, ArgT&& argt, F&& f, Fs&&... fs) {
        return tuple_cat(make_tuple(f(get<offset+I>(forward<ArgT>(argt))...)),
                         call<offset+sizeof...(Is)>(index_sequence<>{},
                                                    forward<ArgT>(argt),
                                                    forward<Fs>(fs)...));}

    template <size_t offset, size_t... Is, typename ArgT, typename... Fs>
    constexpr auto call(index_sequence<Is...>, ArgT&& argt, Fs&&... fs) {
        return call<offset>(index_sequence<Is..., sizeof...(Is)>{},
                            forward<ArgT>(argt), forward<Fs>(fs)...);}
}
template <typename ArgT, typename... Fs>
constexpr auto multifunction(ArgT&& argt, Fs&&... fs) {
    return detail::call<0>(std::index_sequence<>{},
                           std::forward<ArgT>(argt), std::forward<Fs>(fs)...);}

Demo。但是,上面的返回值的数量具有二次时间复杂度,因为tuple_cat是递归调用的。相反,我们可以使用call 的略微修改版本来获取每个调用的索引——然后直接获取实际的tuple

#include <tuple>

namespace detail {
    using namespace std;
    template <size_t, size_t... Is, typename Arg>
    constexpr auto indices(index_sequence<Is...>, Arg&&) {return tuple<>{};}

    template <size_t offset, size_t... Is, typename ArgT, typename... Fs>
    constexpr auto indices(index_sequence<Is...>, ArgT&&, Fs&&...);

    template <size_t offset, size_t... Is, typename ArgT, typename F, class... Fs,
             typename=decltype(declval<F>()(get<offset+Is>(declval<ArgT>())...))>
    constexpr auto indices(index_sequence<Is...>, ArgT&& argt, F&& f, Fs&&... fs){
        return tuple_cat(make_tuple(index_sequence<offset+Is...>{}),
                         indices<offset+sizeof...(Is)>(index_sequence<>{},
                                                       forward<ArgT>(argt),
                                                       forward<Fs>(fs)...));}

    template <size_t offset, size_t... Is, typename ArgT, typename... Fs>
    constexpr auto indices(index_sequence<Is...>, ArgT&& argt, Fs&&... fs) {
        return indices<offset>(index_sequence<Is..., sizeof...(Is)>{},
                            forward<ArgT>(argt), forward<Fs>(fs)...);}

    template <typename Arg, typename F, size_t... Is>
    constexpr auto apply(Arg&& a, F&& f, index_sequence<Is...>) {
        return f(get<Is>(a)...);}

    template <typename ITuple, typename Args, size_t... Is, typename... Fs>
    constexpr auto apply_all(Args&& args, index_sequence<Is...>, Fs&&... fs) {
        return make_tuple(apply(forward<Args>(args), forward<Fs>(fs),
                          tuple_element_t<Is, ITuple>{})...);
    }
}

template <typename ArgT, typename... Fs>
constexpr auto multifunction(ArgT&& argt, Fs&&... fs) {
    return detail::apply_all<decltype(detail::indices<0>(std::index_sequence<>{},
                                                         std::forward<ArgT>(argt),
                                                         std::forward<Fs>(fs)...))>
             (std::forward<ArgT>(argt), std::index_sequence_for<Fs...>{},
              std::forward<Fs>(fs)...);}

Demo 2.

【讨论】:

  • 是的!没有水平滚动条!
  • 是的,你总是给出最短的解决方案。但 GCC 5.2 抱怨右值初始化无效(次要问题)。
  • @Columbo 所以 not 使用传入的索引显然比实际使用它们更简单。太棒了。
  • @prestokeys 是的。只需将它们包装在 lambda 中:[] (auto&amp;&amp;... args) -&gt; decltype(foo(std::forward&lt;decltype(args)&gt;(args)...)) {return foo(std::forward&lt;decltype(args)&gt;(args)...);}
  • @Columbo 你能否通过给foo 显式重载来测试它是否有效?
【解决方案2】:

从头开始构建并忽略完美转发,这样我就不必打字了。我们需要几个帮手。首先,我们需要一个部分版本的 apply 来获取我们想要应用到函数的元组中的哪些索引:

<class Tuple, class F, size_t... Is>
auto partial_apply(Tuple tuple, F f, std::index_sequence<Is...>) {
    return f(get<Is>(tuple)...);
}

然后,我们需要为元组的每个子集调用该函数。假设我们已经将所有函数和索引都包装在一个元组中:

template <class Tuple, class FsTuple, class IsTuple, size_t... Is>
auto multi_apply(Tuple tuple, FsTuple fs, IsTuple indexes, std::index_sequence<Is...>) {
    return std::make_tuple(
        partial_apply(tuple,
            std::get<Is>(fs),
            std::get<Is>(indexes)
            )...
        );
}

所以在这种情况下,我们希望最终调用multi_apply(tuple, &lt;foo,bar,baz&gt;, &lt;&lt;0,1&gt;,&lt;2,3,4&gt;,&lt;5,6,7&gt;&gt;, &lt;0, 1, 2&gt;)

我们需要知道的只是构建indexes 部分。我们从&lt;2,3,3&gt; 开始。我们需要得到部分和(&lt;0,2,5&gt;)并将其添加到索引序列&lt;&lt;0,1&gt;,&lt;0,1,2&gt;,&lt;0,1,2&gt;&gt;。所以我们可以写一个偏和函数:

template <size_t I>
using size_t_ = std::integral_constant<size_t, I>;

template <class R, size_t N>
R partial_sum_(std::index_sequence<>, R, size_t_<N> ) {
    return R{};
}

template <size_t I, size_t... Is, size_t... Js, size_t S>
auto partial_sum_(std::index_sequence<I, Is...>,
        std::index_sequence<Js...>, size_t_<S> )
{
    return partial_sum_(std::index_sequence<Is...>{},
        std::index_sequence<Js..., S>{}, size_t_<S+I>{} );
}

template <size_t... Is>
auto partial_sum_(std::index_sequence<Is...> is)
{
    return partial_sum_(is, std::index_sequence<>{}, size_t_<0>{} );
};

我们可以使用它来生成我们所有的索引作为一个元组:

template <size_t... Is, size_t N>
auto increment(std::index_sequence<Is...>, size_t_<N> )
{
    return std::index_sequence<Is+N...>{};
}

template <class... Seqs, size_t... Ns>
auto make_all_indexes(std::index_sequence<Ns...>, Seqs... seqs)
{
    return std::make_tuple(increment(seqs, size_t_<Ns>{})...);
}

像这样:

template <size_t... Is, class Tuple, class... Fs>
auto multiFunction(Tuple tuple, Fs... fs)
{
    static_assert(sizeof...(Is) == sizeof...(Fs));
    return multi_apply(tuple,
        std::make_tuple(fs...),
        make_all_indexes(
            partial_sum_(std::index_sequence<Is...>{}),
            std::make_index_sequence<Is>{}...
            ),
        std::make_index_sequence<sizeof...(Is)>{}
        );

}

如果要处理 void 返回,则只需让 partial_apply 返回单个元素的元组(或空元组)并将 multi_apply 中的 make_tuple() 用法更改为 tuple_cat()

【讨论】:

  • @Barry 我写了另一个解决方案,我认为它遵循您的思路,使用部分和:ideone.com/Rl8KKo 我们都在最后的返回行中使用std::make_tuple,但std::make_tuple 显然输出了值以正确的顺序,但以相反的顺序调用函数。
  • @prestokeys 函数参数的求值顺序未指定。
  • @Barry 你是对的。我忘了具体说明。在这个程序中,函数应该从左到右阅读(可能很重要)。我看不出std::make_tuple 的实现最终是如何以相反的顺序调用函数的。看起来我们必须编写自己的 make_tuple 函数来确保函数的从左到右。
  • @prestokeys 因为未指定函数参数的评估顺序。您必须将 make_tuple(f(x)...) 更改为 tuple&lt;decltype(f(x))...&gt;{f(x)...}
【解决方案3】:

这是另一个实现:

template<std::size_t N>
constexpr Array<std::size_t, N> scan(std::size_t const (&a)[N])
{
    Array<std::size_t, N> b{};
    for (int i = 0; i != N - 1; ++i)
        b[i + 1] = a[i] + b[i];
    return b;
}

template<std::size_t O, std::size_t... N, class F, class Tuple>
inline decltype(auto) eval_from(std::index_sequence<N...>, F f, Tuple&& t)
{
    return f(std::get<N + O>(std::forward<Tuple>(t))...);
}

template<std::size_t... O, std::size_t... N, class Tuple, class... F>
inline auto multi_function_impl1(std::index_sequence<O...>, std::index_sequence<N...>, Tuple&& t, F... f)
{
    return pack(eval_from<O>(std::make_index_sequence<N>(), f, std::forward<Tuple>(t))...);
}

template<std::size_t... I, std::size_t... N, class Tuple, class... F>
inline auto multi_function_impl0(std::index_sequence<I...>, std::index_sequence<N...>, Tuple&& t, F... f)
{
    constexpr std::size_t ns[] = {N...};
    constexpr auto offsets = scan(ns);
    return multi_function_impl1(std::index_sequence<offsets[I]...>(), std::index_sequence<N...>(), std::forward<Tuple>(t), f...);
}

template<std::size_t... N, class Tuple, class... F>
auto multi_function(Tuple&& t, F... f)
{
    return multi_function_impl0(std::make_index_sequence<sizeof...(N)>(), std::index_sequence<N...>(), std::forward<Tuple>(t), f...);
}

其中packArray分别类似于std::make_tuplestd::array,但是要克服一些问题:

  • std::make_tuple 衰减它的 args,所以引用丢失了
  • std::array 不能在 c++14 中用 constexpr 编写其元素

DEMO

【讨论】:

    【解决方案4】:

    这是我遵循 T.C. 的建议后的解决方案,添加到我之前的(尽管效率低下)解决方案:

    #include <iostream>
    #include <tuple>
    #include <utility>
    
    struct NoReturnValue {
        friend std::ostream& operator<< (std::ostream& os, const NoReturnValue&) {
            return os << "[no value returned]";
        }
    };
    
    template <std::size_t N, typename Tuple>
    struct TupleHead {
        static auto get (const Tuple& tuple) {  // The subtuple from the first N components of tuple.
            return std::tuple_cat (TupleHead<N-1, Tuple>::get(tuple), std::make_tuple(std::get<N-1>(tuple)));
        }
    };
    
    template <typename Tuple>
    struct TupleHead<0, Tuple> {
        static auto get (const Tuple&) { return std::tuple<>{}; }
    };
    
    template <std::size_t N, typename Tuple>
    struct TupleTail {
        static auto get (const Tuple& tuple) {  // The subtuple from the last N components of tuple.
            return std::tuple_cat (std::make_tuple(std::get<std::tuple_size<Tuple>::value - N>(tuple)), TupleTail<N-1, Tuple>::get(tuple));
        }
    };
    
    template <typename Tuple>
    struct TupleTail<0, Tuple> {
        static auto get (const Tuple&) { return std::tuple<>{}; }
    };
    
    template <typename Tuple, typename F, std::size_t... Is>
    auto functionOnTupleHelper (const Tuple& tuple, F f, const std::index_sequence<Is...>&,
            std::enable_if_t< !std::is_void<std::result_of_t<F(std::tuple_element_t<Is, Tuple>...)>>::value >* = nullptr) {  // This overload is called only if f's return type is not void.
        return std::make_tuple(f(std::get<Is>(tuple)...));  // Thanks to T.C.'s advice on returning a single tuple and then calling std::tuple_cat on all the single tuples.
    }
    
    template <typename Tuple, typename F, std::size_t... Is>
    auto functionOnTupleHelper (const Tuple& tuple, F f, const std::index_sequence<Is...>&,
            std::enable_if_t< std::is_void<std::result_of_t<F(std::tuple_element_t<Is, Tuple>...)>>::value >* = nullptr) {  // This overload is called only if f's return type is void.
        f(std::get<Is>(tuple)...);
        return std::tuple<NoReturnValue>();  // Thanks to T.C.'s advice on returning std::tuple<NoReturnValue>() if the return type of 'f' is void.
    }
    
    template <typename Tuple, typename F>
    auto functionOnTuple (const Tuple& tuple, F f) {
        return functionOnTupleHelper (tuple, f, std::make_index_sequence<std::tuple_size<Tuple>::value>{});
    }
    
    template <typename Tuple, typename... Functions> struct MultiFunction;
    
    template <typename Tuple, typename F, typename... Fs>
    struct MultiFunction<Tuple, F, Fs...> {
        template <std::size_t I, std::size_t... Is>
        static inline auto execute (const Tuple& tuple, const std::index_sequence<I, Is...>&, F f, Fs... fs) {
            const auto headTuple = TupleHead<I, Tuple>::get(tuple);
            const auto tailTuple = TupleTail<std::tuple_size<Tuple>::value - I, Tuple>::get(tuple);
            const auto r = functionOnTuple(headTuple, f);  // Which overload of 'functionOnTupleHelper' is called dedends on whether f's return type is void or not.
            return std::tuple_cat (r, MultiFunction<std::remove_const_t<decltype(tailTuple)>, Fs...>::execute (tailTuple, std::index_sequence<Is...>{}, fs...));  // T.C.'s idea of tuple_cat with all the single return tuples.
        }
    };
    
    template <>
    struct MultiFunction<std::tuple<>> {
        static auto execute (const std::tuple<>&, std::index_sequence<>) { return std::tuple<>(); }
    };
    
    template <std::size_t... Is, typename Tuple, typename... Fs>
    auto multiFunction (const Tuple& tuple, Fs... fs) {
        return MultiFunction<Tuple, Fs...>::execute (tuple, std::index_sequence<Is...>{}, fs...);
    }
    
    // Testing
    template <typename T> int foo (int, char) {std::cout << "foo<T>\n";  return 0;}
    double bar (bool, double, long) {std::cout << "bar\n";  return 3.5;}
    double bar (bool, int) {return 1.4;}
    void voidFunction() {std::cout << "voidFunction\n";}
    template <int...> bool baz (char, short, float) {std::cout << "baz<int...>\n";  return true;}
    
    int main() {
        const auto tuple = std::make_tuple(5, 'a', true, 3.5, 1000, 't', 2, 5.8);
        const auto firstBar = [](bool b, double d, long l) {return bar(b, d, l);};
        const auto t = multiFunction<2,3,0,3> (tuple, foo<bool>, firstBar, voidFunction, baz<2,5,1>);  // Note that since 'bar' has an overload, we have to define 'firstBar' to indicate which 'bar' function we want to use.
        std::cout << std::boolalpha << std::get<0>(t) << ' ' << std::get<1>(t) << ' ' << std::get<2>(t) << ' ' << std::get<3>(t) << '\n';
        // 0 3.5 [no value returned] true
    }
    

    【讨论】:

      【解决方案5】:

      这个解决方案应该具有线性时间复杂度。它使用std::tie 而不是std::make_tuple,因此不会不必要地复制函数和参数。与此处的其他答案相比,我认为它应该很容易理解。

      首先,我们需要一个实用程序来调用一个使用 std::tuple 参数的函数。

      template <typename F, typename Args, std::size_t... Is>
      auto invoke_impl(F const& f, Args const& args, std::index_sequence<Is...>)
      {
          return f(std::get<Is>(args)...);
      }
      
      template <typename F, typename Args>
      auto invoke(F const& f, Args const& args)
      {
          return invoke_impl(f, args, std::make_index_sequence<std::tuple_size<Args>::value>());
      }
      

      其次,我们需要一个实用程序来std::tie 元组元素的子范围。

      template <std::size_t Offset, typename Tuple, std::size_t... Is>
      auto sub_tie_impl(Tuple const& tuple, std::index_sequence<Is...>)
      {
          return std::tie(std::get<Offset + Is>(tuple)...);
      }
      
      template <std::size_t Offset, std::size_t Count, typename Tuple>
      auto sub_tie(Tuple const& tuple)
      {
          return sub_tie_impl<Offset>(tuple, std::make_index_sequence<Count>());
      }
      

      现在我们可以创建实用程序来使用一系列函数来使用 std::tuple 的参数。

      首先我们将函数std::tie 分成一个元组,然后我们将参数列表拆分为子参数列表的参数包,最后我们为每个子参数列表调用一个函数,将结果打包成一个元组然后我们返回。

      template <typename Fs, std::size_t... Is, typename... SubArgs>
      auto consume_impl(Fs const& fs, std::index_sequence<Is...>, SubArgs const&... sub_args)
      {
          return std::make_tuple(invoke(std::get<Is>(fs), sub_args)...);
      }
      
      template <std::size_t, typename Args, typename Fs, typename... SubArgs>
      auto consume_impl(Args const&, Fs const& fs, SubArgs const&... sub_args)
      {
          return consume_impl(fs, std::make_index_sequence<sizeof...(SubArgs)>(), sub_args...);
      }
      
      template <std::size_t Offset, std::size_t Count, std::size_t... Counts,
                typename Args, typename Fs, typename... SubArgs>
      auto consume_impl(Args const& args, Fs const& fs, SubArgs const&... sub_args)
      {
          return consume_impl<Offset + Count, Counts...>(args, fs, sub_args...,
                                                         sub_tie<Offset, Count>(args));
      }
      
      template <std::size_t... Counts, typename Args, typename... Fs>
      auto consume(Args const& args, Fs const&... fs)
      {
          return consume_impl<0, Counts...>(args, std::tie(fs...));
      }
      

      【讨论】:

        【解决方案6】:

        这是借用 Barry 的 partial_apply 想法但完全避免使用他的 partial_sum 函数的另一个解决方案。结果它更短。我认为这在时间复杂度上是线性的。

        #include <iostream>
        #include <tuple>
        #include <utility>
        
        template <std::size_t Offset, typename F, typename Tuple, std::size_t... Is>
        auto partial_apply_impl (F f, const Tuple& tuple, const std::index_sequence<Is...>&) {
            return f(std::get<Offset + Is>(tuple)...);
        }
        
        template <typename Off, typename F, typename Tuple>  // Off must be of type OffsetIndexSequence<A,B> only.
        auto partial_apply (F f, const Tuple& tuple) {
            return partial_apply_impl<Off::value>(f, tuple, typename Off::sequence{});
        }
        
        template <std::size_t Offset, std::size_t Size>
        struct OffsetIndexSequence : std::integral_constant<std::size_t, Offset> {
            using sequence = std::make_index_sequence<Size>;
        };
        
        template <typename Output, std::size_t... Is> struct OffsetIndexSequenceBuilder;
        
        template <template <typename...> class P, typename... Out, std::size_t Offset, std::size_t First, std::size_t... Rest>
        struct OffsetIndexSequenceBuilder<P<Out...>, Offset, First, Rest...> :
            OffsetIndexSequenceBuilder<P<Out..., OffsetIndexSequence<Offset, First>>, Offset + First, Rest...> {};
        
        template <template <typename...> class P, typename... Out, std::size_t Offset>
        struct OffsetIndexSequenceBuilder<P<Out...>, Offset> {
            using type = P<Out...>;
        };
        
        template <std::size_t... Is>
        using offset_index_sequences = typename OffsetIndexSequenceBuilder<std::tuple<>, 0, Is...>::type;
        
        template <typename> struct MultiFunction;
        
        template <template <typename...> class P, typename... Offs>
        struct MultiFunction<P<Offs...>> {
            template <typename ArgsTuple, typename... Fs>
            static auto execute (const ArgsTuple& argsTuple, Fs... fs) {
                using ResultTuple = std::tuple<decltype(partial_apply<Offs>(fs, argsTuple))...>;
                return ResultTuple{partial_apply<Offs>(fs, argsTuple)...};
            }
        };
        
        template <std::size_t... Is, typename ArgsTuple, typename... Fs>
        auto multiFunction (const ArgsTuple& argsTuple, Fs... fs) {
            return MultiFunction<offset_index_sequences<Is...>>::execute(argsTuple, fs...);
        }
        
        // Testing
        int foo (int, char) {std::cout << "foo\n";  return 0;}
        double bar (bool, double, long) {std::cout << "bar\n";  return 3.5;}
        bool baz (char, short, float) {std::cout << "baz\n";  return true;}
        
        int main() {
            const auto tuple = std::make_tuple(5, 'a', true, 3.5, 1000, 't', 2, 5.8);
            const std::tuple<int, double, bool> t = multiFunction<2,3,3> (tuple, foo, bar, baz);  // foo   bar   baz
            std::cout << std::boolalpha << std::get<0>(t) << ' ' << std::get<1>(t) << ' ' << std::get<2>(t) << '\n';  // 0 3.5 true
        }
        

        【讨论】:

          猜你喜欢
          • 2016-05-23
          • 2012-04-07
          • 2018-03-22
          • 1970-01-01
          • 1970-01-01
          • 2018-02-13
          • 2016-03-26
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多