Implementación C++14 make_integer_sequence
Intenté implementar la plantilla de alias C++ 14make_integer_sequence
, que simplifica la creación de la plantilla de clase integer_sequence
.
template< class T, T... I> struct integer_sequence
{
typedef T value_type;
static constexpr size_t size() noexcept { return sizeof...(I) ; }
};
template< class T, T N>
using make_integer_sequence = integer_sequence< T, 0,1,2, ... ,N-1 >; // only for illustration.
Para implementarlo make_integer_sequence
necesitamos una estructura auxiliar make_helper
.
template< class T , class N >
using make_integer_sequence = typename make_helper<T,N>::type;
La implementación make_helper
no es demasiado difícil.
template< class T, T N, T... I >
struct make_helper
{
typedef typename mpl::if_< T(0) == N,
mpl::identity< integer_sequence<T,I...> >,
make_helper< T, N-1, N-1,I...>
>::type;
};
Para probar make_integer_sequence
hice esta función principal:
int main()
{
#define GEN(z,n,temp) \
typedef make_integer_sequence< int, n > BOOST_PP_CAT(int_seq,n) ;
BOOST_PP_REPEAT(256, GEN, ~);
}
Compilé el programa con GCC 4.8.0, en un sistema i5 de cuatro núcleos con 8 GB de RAM. La compilación exitosa tomó 4 segundos.
Pero cuando cambié la macro GEN a:
int main() {
#define GEN(z,n,temp) \
typedef make_integer_sequence< int, n * 4 > BOOST_PP_CAT(int_seq, n) ;
BOOST_PP_REPEAT(256, GEN, ~ );
}
La compilación no tuvo éxito y generó el mensaje de error:
memoria virtual agotada.
¿Alguien podría explicar este error y qué lo causó?
EDITAR:
Simplifiqué la prueba a:
int main()
{
typedef make_integer_sequence< int, 4096 > int_seq4096;
}
Luego compilé exitosamente con GCC 4.8.0 -ftemplate-profundidad=65536.
Sin embargo esta segunda prueba:
int main()
{
typedef make_integer_sequence< int, 16384 > int_seq16384;
}
No se compiló con GCC 4.8.0 -ftemplate-profundidad=65536 y generó el error:
memoria virtual agotada.
Entonces, mi pregunta es, ¿cómo puedo disminuir la creación de instancias profundas de plantillas?
Saludos, Khurshid.
Aquí hay una log N
implementación que ni siquiera necesita una mayor profundidad máxima para las instancias de plantillas y se compila bastante rápido:
// using aliases for cleaner syntax
template<class T> using Invoke = typename T::type;
template<unsigned...> struct seq{ using type = seq; };
template<class S1, class S2> struct concat;
template<unsigned... I1, unsigned... I2>
struct concat<seq<I1...>, seq<I2...>>
: seq<I1..., (sizeof...(I1)+I2)...>{};
template<class S1, class S2>
using Concat = Invoke<concat<S1, S2>>;
template<unsigned N> struct gen_seq;
template<unsigned N> using GenSeq = Invoke<gen_seq<N>>;
template<unsigned N>
struct gen_seq : Concat<GenSeq<N/2>, GenSeq<N - N/2>>{};
template<> struct gen_seq<0> : seq<>{};
template<> struct gen_seq<1> : seq<0>{};
Básicamente soy yo analizando la solución de Xeo: crear un wiki comunitario; si lo agradeces, vota a favor de Xeo .
... simplemente lo modifiqué hasta que sentí que no podía ser más simple, lo renombré y lo agregué value_type
según size()
el Estándar (pero solo no lo hice index_sequence
) integer_sequence
, y el código que funciona con GCC 5.2 -std=c++14
podría ejecutarse sin modificaciones en compiladores más antiguos o de otro tipo con los que estoy atrapado. . Podría ahorrarle a alguien algo de tiempo/confusión.
// based on http://stackoverflow.com/a/17426611/410767 by Xeo
namespace std // WARNING: at own risk, otherwise use own namespace
{
template <size_t... Ints>
struct index_sequence
{
using type = index_sequence;
using value_type = size_t;
static constexpr std::size_t size() noexcept { return sizeof...(Ints); }
};
// --------------------------------------------------------------
template <class Sequence1, class Sequence2>
struct _merge_and_renumber;
template <size_t... I1, size_t... I2>
struct _merge_and_renumber<index_sequence<I1...>, index_sequence<I2...>>
: index_sequence<I1..., (sizeof...(I1)+I2)...>
{ };
// --------------------------------------------------------------
template <size_t N>
struct make_index_sequence
: _merge_and_renumber<typename make_index_sequence<N/2>::type,
typename make_index_sequence<N - N/2>::type>
{ };
template<> struct make_index_sequence<0> : index_sequence<> { };
template<> struct make_index_sequence<1> : index_sequence<0> { };
}
Notas:
La "magia" de la solución de Xeo está en la declaración de
_merge_and_renumber
(concat
en su código) con exactamente dos parámetros, mientras que la especificación expone efectivamente sus paquetes de parámetros individuales.el
typename
...::type
en...struct make_index_sequence : _merge_and_renumber<typename make_index_sequence<N/2>::type, typename make_index_sequence<N - N/2>::type>
evita el error:
invalid use of incomplete type 'struct std::_merge_and_renumber<std::make_index_sequence<1ul>, std::index_sequence<0ul> >'