分割可变参数模板参数

发布于 2024-10-28 04:42:29 字数 224 浏览 8 评论 0原文

如何将可变参数模板参数分成两半?像这样的东西:

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

How do I split variadic template arguments in two halves? Something like:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

成熟的代价 2024-11-04 04:42:29

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 非常相似,例如 std::get(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::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 中?

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

Luc's solution is clean and straightforward, but sorely lacks fun.
Because there is only one proper way to use variadic templates and it is to abuse them to do crazy overcomplicated metaprogramming stuff :)

Like this :

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

It is quite short, except that we need to code some helper :

First we need the structure make_pack_indices, which is used to generate a range of integer at compile-time. For example make_pack_indices<5, 0>::type is actually the 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;
};

We also need a get() function, very similar to std::get for tuple, such as std::get<N>(ts...) return the Nth element of a parameters pack.

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

But to build get() we also need a pack_element helper structure, again very similar to std::tuple_element, such as pack_element<N, Ts...>::type is the Nth type of the parameters pack.

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

And here we go.
Actually I don't really understand why pack_element and get() are not in the standard library already. Those helpers are present for std::tuple, why not for parameters pack ?

Note : My implementation of pack_element and make_pack_indices is a direct transposition of std::tuple_element and __make_tuple_indices implementation found in libc++.

向日葵 2024-11-04 04:42:29

我们仍然缺乏很多助手来操作可变参数包(或者我不知道它们)。在一个好的 Boost 库将它们带给我们之前,我们仍然可以编写自己的库。

例如,如果您愿意将数组的初始化推迟到构造函数主体,则可以创建并使用一个将部分参数包复制到输出迭代器的函数:

#include <array>
#include <cassert>
#include <iostream>

// Copy n values from the parameter pack to an output iterator
template < typename OutputIterator >
void copy_n( size_t n, OutputIterator )
{
  assert ( n == 0 );
}

template < typename OutputIterator, typename T, typename... Args >
void copy_n( size_t n, OutputIterator out, const T & value, Args... args )
{
  if ( n > 0 )
  {
    *out = value;
    copy_n( n - 1, ++out, args... );
  }
}

// Copy n values from the parameter pack to an output iterator, starting at
// the "beginth" element
template < typename OutputIterator >
void copy_range( size_t begin, size_t size, OutputIterator out )
{
  assert( size == 0 );
}


template < typename OutputIterator, typename T, typename... Args >
void copy_range( size_t begin, size_t size, OutputIterator out, T value, Args... args )
{
  if ( begin == 0 )
  {
    copy_n( size, out, value, args... );
  }
  else
  {
    copy_range( begin - 1, size, out, args... );
  }
}


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

  template < typename... Args >
  DoubleArray ( Args... args )
  {
    copy_range( 0, N, p.begin(), args... );
    copy_range( N, N, q.begin(), args... );
  } 

};

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

如您所见,您可以(不那么)轻松地创建您的数组。自己的算法来操作参数包;所需要的只是对递归和模式匹配的良好理解(与进行模板元编程时一样)。

We still lack a lot of helpers to manipulate variadic parameter packs (or I am not aware of them). Until a nice Boost library brings them to us, we can still write our own.

For example, if you are willing to postpone your array's initialization to the constructor body, you can create and use a function that copies part of the parameter pack to an output iterator:

#include <array>
#include <cassert>
#include <iostream>

// Copy n values from the parameter pack to an output iterator
template < typename OutputIterator >
void copy_n( size_t n, OutputIterator )
{
  assert ( n == 0 );
}

template < typename OutputIterator, typename T, typename... Args >
void copy_n( size_t n, OutputIterator out, const T & value, Args... args )
{
  if ( n > 0 )
  {
    *out = value;
    copy_n( n - 1, ++out, args... );
  }
}

// Copy n values from the parameter pack to an output iterator, starting at
// the "beginth" element
template < typename OutputIterator >
void copy_range( size_t begin, size_t size, OutputIterator out )
{
  assert( size == 0 );
}


template < typename OutputIterator, typename T, typename... Args >
void copy_range( size_t begin, size_t size, OutputIterator out, T value, Args... args )
{
  if ( begin == 0 )
  {
    copy_n( size, out, value, args... );
  }
  else
  {
    copy_range( begin - 1, size, out, args... );
  }
}


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

  template < typename... Args >
  DoubleArray ( Args... args )
  {
    copy_range( 0, N, p.begin(), args... );
    copy_range( N, N, q.begin(), args... );
  } 

};

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

As you can see, you can (not so) easily create your own algorithms to manipulate parameter packs; all is needed is a good understanding of recursion and pattern matching (as always when doing Template MetaProgramming).

匿名。 2024-11-04 04:42:29

请注意,在这种特殊情况下,您可以使用 std::initializer_list

template<int... Is> struct index_sequence{};

template<int N, int... Is> struct make_index_sequence
{
    typedef typename make_index_sequence<N - 1, N - 1, Is...>::type type;
};

template<int... Is> struct make_index_sequence<0, Is...>
{
    typedef index_sequence<Is...> type;
};

template <int d> struct a {
    std::array <int, d> p, q;

    constexpr a (const std::initializer_list<int>& t) :
        a(t, typename make_index_sequence<d>::type())
    {}

private:
    template <int... Is>
    constexpr a(const std::initializer_list<int>& t, index_sequence<Is...>) :
        p ({{(*(t.begin() + Is))...}}),
        q ({{(*(t.begin() + d + Is))...}})
    {}
};

Note that in this particular case, you may use std::initializer_list:

template<int... Is> struct index_sequence{};

template<int N, int... Is> struct make_index_sequence
{
    typedef typename make_index_sequence<N - 1, N - 1, Is...>::type type;
};

template<int... Is> struct make_index_sequence<0, Is...>
{
    typedef index_sequence<Is...> type;
};

template <int d> struct a {
    std::array <int, d> p, q;

    constexpr a (const std::initializer_list<int>& t) :
        a(t, typename make_index_sequence<d>::type())
    {}

private:
    template <int... Is>
    constexpr a(const std::initializer_list<int>& t, index_sequence<Is...>) :
        p ({{(*(t.begin() + Is))...}}),
        q ({{(*(t.begin() + d + Is))...}})
    {}
};
居里长安 2024-11-04 04:42:29

这是另一个解决方案:

#include <array>
#include <tuple>
#include <iostream>

template <int i, int o> struct cpyarr_ {
  template < typename T, typename L > static void f (T const& t, L &l) {
    l[i-1] = std::get<i-1+o> (t);
    cpyarr_<i-1,o>::f (t,l);
  }
};

template <int o> struct cpyarr_ <0,o> {
  template < typename T, typename L > static void f (T const&, L&) {}
};

template <int i, int o, typename U, typename ... T> std::array < U, i > cpyarr (U u, T... t) {
  std::tuple < U, T... > l { u, t... };
  std::array < U, i > a; 
  cpyarr_<i,o>::f (l, a); // because std::copy uses call to memmov which is not optimized away (at least with g++ 4.6)
  return a;
}

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

int main () {
  a <5> x { 0,1,2,3,4,5,6,7,8,9 };
  for (int i = 0; i < 5; i++)
    std::cout << x.p[i] << " " << x.q[i] << "\n";
}

Here is yet another solution:

#include <array>
#include <tuple>
#include <iostream>

template <int i, int o> struct cpyarr_ {
  template < typename T, typename L > static void f (T const& t, L &l) {
    l[i-1] = std::get<i-1+o> (t);
    cpyarr_<i-1,o>::f (t,l);
  }
};

template <int o> struct cpyarr_ <0,o> {
  template < typename T, typename L > static void f (T const&, L&) {}
};

template <int i, int o, typename U, typename ... T> std::array < U, i > cpyarr (U u, T... t) {
  std::tuple < U, T... > l { u, t... };
  std::array < U, i > a; 
  cpyarr_<i,o>::f (l, a); // because std::copy uses call to memmov which is not optimized away (at least with g++ 4.6)
  return a;
}

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

int main () {
  a <5> x { 0,1,2,3,4,5,6,7,8,9 };
  for (int i = 0; i < 5; i++)
    std::cout << x.p[i] << " " << x.q[i] << "\n";
}
塔塔猫 2024-11-04 04:42:29

我知道这个问题很老了,但我昨天在寻找一个非常类似问题的解决方案时才发现它。我自己制定了一个解决方案,并最终编写了一个小型库,我相信它可以满足您的需求。您可以在此处找到说明如果您仍然感兴趣。

I know this question is quite old, but I found it only yesterday while looking for a solution to a very similar problem. I worked out a solution myself and ended up writing a small library which I believe does what you want. You can find a description here if you are still interested.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文