25

如何将参数包分成两个相等的部分?

例如,我想做这样的事情:

template<typename T> T sum(const T& t)
{ return t; }

template<typename T> T sum(const T& t1, const T& t2)
{ return t1 + t2; }

template<typename ...T> T sum(T&& ...t)
{ sum(first_half(t)...) + sum(second_half(t)...); }
4

2 回答 2

4

我会建议一些类似的东西,因为所需的嵌套深度和样板代码的数量低于建议的解决方案。但是,实际参数包永远不会拆分,而是生成两个索引范围来索引输入值,这些输入值作为元组转发,然后通过 std::get 访问。除了嵌套深度之外,更容易指定如何执行拆分(向上、向下舍入或取 2 的幂和余数)。

int sum(int a) { return a; }
int sum(int a, int b) { return a + b; }

template<typename... Args> int sum(Args&&... args);

template<typename Tuple, size_t... index>
int sum_helper(pack_indices<index...>, Tuple&& args)
{
    return sum(std::get<index>(args)...);
}

template <size_t begin, size_t end, typename... Args>
int sum_helper(Args&&... args)
{
    typename make_pack_indices<end, begin>::type indices;
    return sum_helper(indices, std::forward_as_tuple(std::forward<Args>(args)...));
}

template<typename... Args>
int sum(Args&&... args)
{
    constexpr size_t N = sizeof...(Args);
    return sum(
        sum_helper<0, N/2>(std::forward<Args>(args)...),
        sum_helper<N/2, N>(std::forward<Args>(args)...)
    );
}

这需要

template <size_t...>
struct pack_indices {};

template <size_t Sp, typename IntPack, size_t Ep>
struct make_indices_imp;

template <size_t Sp, size_t Ep, size_t... Indices>
struct make_indices_imp<Sp, pack_indices<Indices...>, Ep>
{
    typedef typename make_indices_imp<Sp+1, pack_indices<Indices..., Sp>, Ep>::type type;
};

template <size_t Ep, size_t... Indices>
struct make_indices_imp<Ep, pack_indices<Indices...>, Ep>
{
    typedef pack_indices<Indices...> type;
};

template <size_t Ep, size_t Sp = 0>
struct make_pack_indices
{
    static_assert(Sp <= Ep, "make_tuple_indices input error");
    typedef typename make_indices_imp<Sp, pack_indices<>, Ep>::type type;
};
于 2013-06-07T09:40:46.547 回答
2

一种可能的解决方案是将参数列表转换为元组,然后通过std::getand提取所需的参数std::index_sequence(它只会出现在 C++14 中,但同时您可以轻松实现相同的功能)。

未经测试的示例代码如下:

template<class T1, class T2>
struct append_index_seq;

template<std::size_t N, std::size_t... NN>
struct append_index_seq<N, std::index_sequence<NN...>> {
    using type = std::index_sequence<N, NN...>;
};

template<std::size_t M, std::size_t N1, std::size_t... N>
struct add_index_seq_impl {
    using type = append_index_seq<N1+M, add_index_seq<N, M>::type>::type;
};

template<std::size_t M, std::size_t N1>
struct add_index_seq_impl {
    using type = std::index_sequence<N1+M>::type;
};

template<std::size_t M, std::size_t... N>
struct add_index_seq;

template<std::size_t M, std::size_t... N>
struct add_index_seq<m, std::index_sequence<N...>> {
    using type = add_index_seq_impl<M, N...>;
}

template<std::size_t N>
struct get_first_half {
    static_assert(N % 2 == 0, "N must be even");
    using indexes = std::make_index_sequence<N/2>;
};

template<std::size_t N>
struct get_second_half {
    static_assert(N % 2 == 0, "N must be even");
    using indexes_1st = std::make_index_sequence<N/2>;
    using indexes = add_index_seq<N/2, indexes_1st>::type;
};

template<class F, class Tuple, std::size_t... I>
auto apply(F&& f, Tuple&& t, index_sequence<I...>) 
{
    return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(t))...);
}

template<class ...T> T sum(T&& ...t)
{ 
     auto params = std::make_tuple(t);
     T r1 = apply(sum, params, get_first_half<T...>::indexes);
     T r2 = apply(sum, params, get_second_half<T...>::indexes);
     return r1 + r2;
}
于 2013-06-05T10:24:05.733 回答