разделить аргументы шаблона с переменным числом вариантов
-
14-11-2019 - |
Вопрос
Как разделить аргументы шаблона с переменным числом вариантов на две половины?Что-то вроде:
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...}) {}
};
Решение
Нам все еще не хватает многих помощников, чтобы манипулировать вариационными пакетами параметров (или я не знаю их).Пока приятная библиотека принесет их к нам, мы все еще можем написать нашим собственным.
Например, если вы готовы отложить свои массивы инициализации на корпус конструктора, вы можете создавать и использовать бонк, что копирует часть пакета параметра на вывод 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;
std::cout << mya.q[0] << mya.q[2] << std::endl;
}
// Ouput:
// 13
// 46
.
Как вы можете видеть, вы можете (не так) легко создавать свои собственные алгоритмы для манипулирования пакетами параметров;Все нужно, это хорошее понимание сопоставления рекурсионного и шаблона (как всегда, когда делает шаблон MetaProgramming ...).
Другие советы
Решение Люка чистое и простое, но ему крайне не хватает веселья.
Потому что есть только один правильный способ использовать вариативные шаблоны - злоупотреблять ими, чтобы делать сумасшедшие и сверхсложные вещи метапрограммирования :)
Так :
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<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() нам также нужна вспомогательная структура package_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;
};
И здесь мы идем.
На самом деле я не совсем понимаю, почему package_element и get() еще не включены в стандартную библиотеку.Эти помощники присутствуют для std::tuple, почему бы не для пакета параметров?
Примечание :Моя реализация package_element и make_pack_indices представляет собой прямую транспозицию реализации std::tuple_element и __make_tuple_indices, найденной в libc++.
Обратите внимание, что в этом конкретном случае вы можете использовать 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))...}})
{}
};
. Вот еще одно решение:
#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";
}
. Я знаю, что этот вопрос довольно старый, но я нашел его только вчера, ищу решение очень похожую проблему.Я сам разработал решение и закончил писать небольшую библиотеку, которую я считаю, что вы хотите.Вы можете найти описание здесь Если вы все еще заинтересованы.