Разделенные вариационные аргументы шаблона

Как разделить вариационные аргументы шаблона в две половины? Что-то вроде:

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

Ответ 1

Нам по-прежнему не хватает помощников для манипулирования пакетами вариативных параметров (или я не знаю о них). Пока хорошая библиотека Boost не принесет их нам, мы все равно можем написать свои собственные.

Например, если вы хотите отложить инициализацию массивов в тело конструктора, вы можете создать и использовать функцию fonction, которая копирует часть пакета параметров в выходной итератор:

#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;
  std::cout << mya.q[0] << mya.q[2] << std::endl;
}

// Ouput:
// 13
// 46

Как вы можете видеть, вы можете (не так) легко создавать свои собственные алгоритмы для управления пакетами параметров; все необходимо, это хорошее понимание рекурсии и сопоставления шаблонов (как всегда при выполнении шаблона MetaProgramming...).

Ответ 2

Решение 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 для 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 является прямой транспозицией реализации std:: tuple_element и __make_tuple_indices, найденной в libС++.

Ответ 3

Обратите внимание, что в этом конкретном случае вы можете использовать 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))...}})
    {}
};

Ответ 4

Вот еще одно решение:

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

Ответ 5

Я знаю, что этот вопрос довольно старый, но я нашел его только вчера, ища решение очень похожей проблемы. Я сам разработал решение и в итоге написал небольшую библиотеку, которая, как я считаю, делает то, что вы хотите. Вы можете найти описание здесь, если вы все еще заинтересованы.