J'ai essayé d'implémenter le modèle d'alias C++ 14make_integer_sequence
, ce qui simplifie la création du modèle de classe 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.
Implémenter make_integer_sequence
nous avons besoin d'une structure d'aide make_helper
.
template< class T , class N >
using make_integer_sequence = typename make_helper<T,N>::type;
Implémentation de make_helper
n'est pas trop difficile.
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;
};
Tester make_integer_sequence
J'ai fait cette fonction principale:
int main()
{
#define GEN(z,n,temp) \
typedef make_integer_sequence< int, n > BOOST_PP_CAT(int_seq,n) ;
BOOST_PP_REPEAT(256, GEN, ~);
}
J'ai compilé le programme avec GCC 4.8.0, sur un système quadricœur i5 avec 8 Go de RAM. La compilation réussie a pris 4 secondes.
Mais, quand j'ai changé la macro GEN en:
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 compilation a échoué et a généré le message d'erreur:
mémoire virtuelle épuisée.
Quelqu'un pourrait-il expliquer cette erreur et quelle en est la cause?
ÉDITER:
J'ai simplifié le test pour:
int main()
{
typedef make_integer_sequence< int, 4096 > int_seq4096;
}
J'ai ensuite compilé avec succès avec GCC 4.8.0 -ftemplate-depth = 65536.
Cependant ce deuxième test:
int main()
{
typedef make_integer_sequence< int, 16384 > int_seq16384;
}
N'a pas compilé avec GCC 4.8.0 -ftemplate-depth = 65536 et a entraîné l'erreur:
mémoire virtuelle épuisée.
Donc, ma question est, comment puis-je réduire l'instanciation profonde du modèle?
Cordialement, Khurshid.
Voici un log N
implémentation qui n'a même pas besoin d'une profondeur maximale accrue pour les instanciations de modèles et se compile assez rapidement:
// 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>{};
C'est essentiellement moi qui pirate la solution de Xeo: Rendre le wiki communautaire - si appréciable, s'il vous plaît, voter Xeo .
... juste modifié jusqu'à ce que je pense que cela ne pourrait pas être plus simple, renommé et ajouté value_type
et size()
selon la norme (mais seulement en faisant index_sequence
pas integer_sequence
), Et le code fonctionnant avec GCC 5.2 -std=c++14
Pourrait s'exécuter autrement sans être altéré sous les anciens/autres compilateurs avec lesquels je suis coincé. Cela pourrait faire gagner du temps/de la confusion à quelqu'un.
// 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> { };
}
Remarques:
la "magie" de la solution de Xeo réside dans la déclaration de _merge_and_renumber
(concat
dans son code) avec exactement deux paramètres, tandis que la spécilisation expose efficacement leurs packs de paramètres individuels
le typename
... ::type
dans ...
struct make_index_sequence
: _merge_and_renumber<typename make_index_sequence<N/2>::type,
typename make_index_sequence<N - N/2>::type>
évite l'erreur:
invalid use of incomplete type 'struct std::_merge_and_renumber<std::make_index_sequence<1ul>, std::index_sequence<0ul> >'
J'ai trouvé une version de récursion profonde très rapide et inutile de l'implémentation de make_index_sequence
. Dans mon PC, il compile avec N = 1 048 576, avec 2 s. (PC: Centos 6.4 x86, i5, 8 Go de RAM, gcc-4.4.7 -std = c ++ 0x -O2 -Wall).
#include <cstddef> // for std::size_t
template< std::size_t ... i >
struct index_sequence
{
typedef std::size_t value_type;
typedef index_sequence<i...> type;
// gcc-4.4.7 doesn't support `constexpr` and `noexcept`.
static /*constexpr*/ std::size_t size() /*noexcept*/
{
return sizeof ... (i);
}
};
// this structure doubles index_sequence elements.
// s- is number of template arguments in IS.
template< std::size_t s, typename IS >
struct doubled_index_sequence;
template< std::size_t s, std::size_t ... i >
struct doubled_index_sequence< s, index_sequence<i... > >
{
typedef index_sequence<i..., (s + i)... > type;
};
// this structure incremented by one index_sequence, iff NEED-is true,
// otherwise returns IS
template< bool NEED, typename IS >
struct inc_index_sequence;
template< typename IS >
struct inc_index_sequence<false,IS>{ typedef IS type; };
template< std::size_t ... i >
struct inc_index_sequence< true, index_sequence<i...> >
{
typedef index_sequence<i..., sizeof...(i)> type;
};
// helper structure for make_index_sequence.
template< std::size_t N >
struct make_index_sequence_impl :
inc_index_sequence< (N % 2 != 0),
typename doubled_index_sequence< N / 2,
typename make_index_sequence_impl< N / 2> ::type
>::type
>
{};
// helper structure needs specialization only with 0 element.
template<>struct make_index_sequence_impl<0>{ typedef index_sequence<> type; };
// OUR make_index_sequence, gcc-4.4.7 doesn't support `using`,
// so we use struct instead of it.
template< std::size_t N >
struct make_index_sequence : make_index_sequence_impl<N>::type {};
//index_sequence_for any variadic templates
template< typename ... T >
struct index_sequence_for : make_index_sequence< sizeof...(T) >{};
// test
typedef make_index_sequence< 1024 * 1024 >::type a_big_index_sequence;
int main(){}
Il vous manque un -1
ici:
typedef typename mpl::if_< T(0) == N,
mpl::identity< integer_sequence<T> >,
make_helper< T, N, N-1,I...>
>::type;
en particulier:
typedef typename mpl::if_< T(0) == N,
mpl::identity< integer_sequence<T> >,
make_helper< T, N-1, N-1,I...>
>::type;
Ensuite, la première branche ne doit pas être integer_sequence<T>
, mais plutôt integer_sequence<T, I...>
.
typedef typename mpl::if_< T(0) == N,
mpl::identity< integer_sequence<T, I...> >,
make_helper< T, N-1, N-1,I...>
>::type;
ce qui devrait être suffisant pour que votre code d'origine soit compilé.
En général, lors de l'écriture d'une métaprogrammation sérieuse template
, votre objectif principal doit être de réduire la profondeur de l'instanciation de template
. Une façon de penser à ce problème consiste à imaginer que vous avez un ordinateur à thread infini: chaque calcul indépendant doit être mélangé sur son propre thread, puis mélangé à la fin. Vous avez quelques opérations qui prennent O(1) profondeur, comme ...
expansion: exploitez-les.
Habituellement, tirer de la profondeur logarithmique est suffisant, car avec un 900
profondeur, qui permet 2^900
structures de taille, et quelque chose d'autre se brise en premier. (Pour être honnête, il est plus probable que 90 couches différentes de 2^10
structures de taille).
Voici une autre variation légèrement plus générale de la réponse logarithmique de Xeo qui fournit make_integer_sequence
pour les types arbitraires. Pour ce faire, utilisez std::integral_constant
afin d'éviter l'argument redouté "l'argument de modèle implique un paramètre de modèle".
template<typename Int, Int... Ints>
struct integer_sequence
{
using value_type = Int;
static constexpr std::size_t size() noexcept
{
return sizeof...(Ints);
}
};
template<std::size_t... Indices>
using index_sequence = integer_sequence<std::size_t, Indices...>;
namespace
{
// Merge two integer sequences, adding an offset to the right-hand side.
template<typename Offset, typename Lhs, typename Rhs>
struct merge;
template<typename Int, Int Offset, Int... Lhs, Int... Rhs>
struct merge<
std::integral_constant<Int, Offset>,
integer_sequence<Int, Lhs...>,
integer_sequence<Int, Rhs...>
>
{
using type = integer_sequence<Int, Lhs..., (Offset + Rhs)...>;
};
template<typename Int, typename N>
struct log_make_sequence
{
using L = std::integral_constant<Int, N::value / 2>;
using R = std::integral_constant<Int, N::value - L::value>;
using type = typename merge<
L,
typename log_make_sequence<Int, L>::type,
typename log_make_sequence<Int, R>::type
>::type;
};
// An empty sequence.
template<typename Int>
struct log_make_sequence<Int, std::integral_constant<Int, 0>>
{
using type = integer_sequence<Int>;
};
// A single-element sequence.
template<typename Int>
struct log_make_sequence<Int, std::integral_constant<Int, 1>>
{
using type = integer_sequence<Int, 0>;
};
}
template<typename Int, Int N>
using make_integer_sequence =
typename log_make_sequence<
Int, std::integral_constant<Int, N>
>::type;
template<std::size_t N>
using make_index_sequence = make_integer_sequence<std::size_t, N>;
Démo: colir
Implémentation simple O (N). Probablement pas ce que vous voulez pour un grand N, mais mon application est uniquement pour appeler des fonctions avec des arguments indexés, et je ne m'attendrais pas à une arité supérieure à environ 10. Je n'ai pas rempli les membres de integer_sequence. J'ai hâte d'utiliser une implémentation de bibliothèque standard et de nuquer à cela :)
template <typename T, T... ints>
struct integer_sequence
{ };
template <typename T, T N, typename = void>
struct make_integer_sequence_impl
{
template <typename>
struct tmp;
template <T... Prev>
struct tmp<integer_sequence<T, Prev...>>
{
using type = integer_sequence<T, Prev..., N-1>;
};
using type = typename tmp<typename make_integer_sequence_impl<T, N-1>::type>::type;
};
template <typename T, T N>
struct make_integer_sequence_impl<T, N, typename std::enable_if<N==0>::type>
{ using type = integer_sequence<T>; };
template <typename T, T N>
using make_integer_sequence = typename make_integer_sequence_impl<T, N>::type;