拆分argpack一半?

时间:2013-05-29 18:49:06

标签: c++ templates c++11 variadic-templates

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

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

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

2 个答案:

答案 0 :(得分: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;
};

答案 1 :(得分:2)

一种可能的解决方案是将参数列表转换为元组,然后通过std::getstd::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;
}