c++ - 拆分可变参数模板参数

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

如何将可变参数模板参数分成两半?比如:

template <int d> struct a {
  std::array <int, d> p, q;
  template <typename ... T> a (T ... t) : p ({half of t...}), q ({other half of t...}) {} 
};

最佳答案

Luc 的解决方案简洁明了,但非常缺乏乐趣。
因为只有一种使用可变参数模板的正确方法,那就是滥用它们来做疯狂的过度复杂的元编程工作:)

像这样:

template <class T, size_t... Indx, class... Ts>
std::array<T, sizeof...(Indx)>
split_array_range_imp(pack_indices<Indx...> pi, Ts... ts)
{
    return std::array<T, sizeof...(Indx)>{get<Indx>(ts...)...}; //TADA
}


template <class T, size_t begin, size_t end, class... Ts>
std::array<T, end - begin>
split_array_range(Ts... ts)
{
    typename make_pack_indices<end, begin>::type indices;
    return split_array_range_imp<T>(indices, ts...);
}

template <size_t N>
struct DoubleArray
{
  std::array <int, N> p, q;

  template <typename ... Ts>
  DoubleArray (Ts ... ts) :
  p( split_array_range<int, 0                , sizeof...(Ts) / 2 >(ts...) ),
  q( split_array_range<int, sizeof...(Ts) / 2, sizeof...(Ts)     >(ts...) )
  {
  }
};

int main()
{
    DoubleArray<3> mya{1, 2, 3, 4, 5, 6};
    std::cout << mya.p[0] << "\n" << mya.p[1] << "\n" << mya.p[2] << std::endl;
    std::cout << mya.q[0] << "\n" << mya.q[1] << "\n" << mya.q[2] << std::endl;
}

很短,只是我们需要编写一些帮助程序:

首先我们需要结构体make_pack_indices,它用于在编译时生成整数范围。例如 make_pack_indices<5, 0>::type实际上是类型pack_indices<0, 1, 2, 3, 4>

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

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

template <size_t Sp, size_t ... Indices, size_t Ep>
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;
};

我们还需要一个get()函数,非常类似于std::get for tuple,比如std::get<N>(ts...)返回参数包的第 N 个元素。

template <class R, size_t Ip, size_t Ij, class... Tp>
struct Get_impl
{
    static R& dispatch(Tp...);
};

template<class R,  size_t Ip, size_t Jp, class Head, class... Tp>
struct Get_impl<R, Ip, Jp, Head, Tp...>
{
    static R& dispatch(Head& h, Tp&... tps)
    {
        return Get_impl<R, Ip, Jp + 1, Tp...>::dispatch(tps...);
    }
};

template<size_t Ip, class Head, class... Tp>
struct Get_impl<Head, Ip, Ip, Head, Tp...>
{
    static Head& dispatch(Head& h, Tp&... tps)
    {
        return h;
    }
};


template <size_t Ip, class ... Tp>
typename pack_element<Ip, Tp...>::type&
get(Tp&... tps)
{
    return Get_impl<typename pack_element<Ip, Tp...>::type, Ip, 0, Tp...>::dispatch(tps...);
}

但要构建 get(),我们还需要一个 pack_element 辅助结构,再次与 std::tuple_element 非常相似,例如 pack_element<N, Ts...>::type是参数包的第 N 种类型。

template <size_t _Ip, class _Tp>
class pack_element_imp;

template <class ..._Tp>
struct pack_types {};

template <size_t Ip>
class pack_element_imp<Ip, pack_types<> >
{
public:
    static_assert(Ip == 0, "tuple_element index out of range");
    static_assert(Ip != 0, "tuple_element index out of range");
};

template <class Hp, class ...Tp>
class pack_element_imp<0, pack_types<Hp, Tp...> >
{
public:
    typedef Hp type;
};

template <size_t Ip, class Hp, class ...Tp>
class pack_element_imp<Ip, pack_types<Hp, Tp...> >
{
public:
    typedef typename pack_element_imp<Ip-1, pack_types<Tp...> >::type type;
};

template <size_t Ip, class ...Tp>
class pack_element
{
public:
    typedef typename pack_element_imp<Ip, pack_types<Tp...> >::type type;
};

我们开始吧。
其实我真的不明白为什么 pack_element 和 get() 已经不在标准库中了。这些帮助器存在于 std::tuple 中,为什么不用于参数包?

注意:我对 pack_element 和 make_pack_indices 的实现是对 libc++ 中的 std::tuple_element 和 __make_tuple_indices 实现的直接转换。

关于c++ - 拆分可变参数模板参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5484930/

相关文章:

c++ - 如何为这个例子中的所有参数设置相同的类型?

c++ - 可变参数 C++ 模板解压后终止?

c++ - 跳转绕过 switch 语句中的变量初始化

ARM 的 C++ 异常处理 - 通用异常处理表条目的格式

c++ - 为什么 Netbeans 无法识别 `cbegin()` 、 `cend()` 、 `unordered_set` 以及其他 C++ 功能?

c++ - 如果 std::call_once 已执行,获取状态吗?

c++ - 每个 lambda 函数都是匿名类吗?

c++ - 可变参数模板类型解包到映射键中

c++ - 带有可变参数模板的 std::lock_guard

c++ - 标准容器上带有迭代器的特定路线