2016-09-09 3 views
4

Supposons que nous ayons un code comme celui-ci. Cela fonctionne bien et pré-calcule les 5 premiers nombres de Fibonacci.calcul de l'heure de compilation avec métaprogrammation de gabarit

#include <iostream> 

template <int T> 
struct fib; 

template <> 
struct fib<0>{ 
    constexpr static int value = 1; 
}; 

template <> 
struct fib<1>{ 
    constexpr static int value = 1; 
}; 

template <int I> 
struct fib{ 
    constexpr static int value = fib<I - 1>::value + fib<I - 2>::value; 
}; 

int main(){ 
    std::cout << fib<0>::value << std::endl; 
    std::cout << fib<1>::value << std::endl; 
    std::cout << fib<2>::value << std::endl; 
    std::cout << fib<3>::value << std::endl; 
    std::cout << fib<4>::value << std::endl; 
    std::cout << fib<5>::value << std::endl; 
} 

Cependant, il y a un "petit" problème. Et si nous avions besoin d'utiliser ceci pour des valeurs qui ne sont pas connues au moment de la compilation?

Pour quelques valeurs que nous pouvons faire:

const int max = 5; 

int getData(){ 
    return 5; // return value between 0 and max. 
} 

int something(){ 
    switch(getData()){ 
     case 0: return fib<0>::value; 
     case 1: return fib<1>::value; 
     case 2: return fib<2>::value; 
     case 3: return fib<3>::value; 
     case 4: return fib<4>::value; 
     case 5: return fib<5>::value; 
    } 
} 

Cette volonté fonctionne bien pour 5 valeurs, mais si nous avons 150 ou 300?
Ce n'est pas vraiment grave de changer le code avec 300 lignes ...

Quelle pourrait être la solution ici?

+1

Vous pourrait créer un tableau statique et rechercher dans l'exécution selon e. g. http://stackoverflow.com/questions/37297359/sequence-array-initialization-with-template – xcvii

Répondre

4

Si vous devez utiliser une valeur inconnue à la compilation lors de l'exécution, vous ne pouvez pas la calculer au moment de la compilation. Évident.

Mais ... si vous pouvez imposer une valeur supérieure aux valeurs nécessaires, vous pouvez calculer toutes valeurs (de zéro en haut) au moment de la compilation et de les stocker dans un std::array.

Dans l'exemple suivant, je l'ai modifié vos fib struct (utiliser un indice std::size_t et un type de modèle (par défaut unsigned long) pour la valeur) et j'ai ajouté un struct fibVals basé sur un modèle contenant un std::array qui est initialisé à l'aide fib<n>::value

Le main() suivant montre qu'il est possible de définir un constexpr fibvals<N> (avec N == 20 dans l'exemple) pour calculer (au moment de la compilation) toutes les valeurs fib<n> dans la plage [0, N [.

#include <array> 
#include <utility> 
#include <iostream> 

template <std::size_t, typename T = unsigned long> 
struct fib; 

template <typename T> 
struct fib<0U, T> 
{ constexpr static T value { T(1) }; }; 

template <typename T> 
struct fib<1U, T> 
{ constexpr static T value { T(1) }; }; 

template <std::size_t I, typename T> 
struct fib 
{ constexpr static T value { fib<I-1U>::value + fib<I-2U>::value }; }; 

template <std::size_t I, typename T = unsigned long> 
struct fibVals 
{ 
    const std::array<T, I> vals; 

    template <std::size_t ... Is> 
    constexpr fibVals (std::index_sequence<Is...> const &) 
     : vals { { fib<Is, T>::value ... } } 
    { } 

    constexpr fibVals() : fibVals { std::make_index_sequence<I> { } } 
    { } 
}; 


int main() 
{ 
    constexpr fibVals<20> fv; 

    for (auto ui = 0U ; ui < fv.vals.size() ; ++ui) 
     std::cout << "fib(" << ui << ") = " << fv.vals[ui] << std::endl; 
} 

Malheureusement cet exemple utiliser std::make_index_sequence<I> et std::index_sequence<Is...> qui sont 14 C++ fonctions.

Si vous voulez mettre en œuvre struct fibVals en C++ 11, vous pouvez mettre en œuvre les struct suivantes struct indexSeq et struct indexSeqHelper, de remplacer std::index_sequence<Is...> et std::make_index_sequence<I>

template <std::size_t ...> 
struct indexSeq 
{ }; 

template <std::size_t N, std::size_t ... Next> 
struct indexSeqHelper 
{ using type = typename indexSeqHelper<N-1U, N-1U, Next ... >::type; }; 

template <std::size_t ... Next > 
struct indexSeqHelper<0U, Next ... > 
{ using type = indexSeq<Next ... >; }; 

et mettre en œuvre fibVals constructeurs comme suit

template <std::size_t ... Is> 
constexpr fibVals (indexSeq<Is...> const &) 
    : vals { { fib<Is, T>::value ... } } 
{ } 

constexpr fibVals() : fibVals { typename indexSeqHelper<I>::type { } } 
{ } 
+0

pouvez-vous m'expliquer ce qui se passe exactement ici: vals {{fib :: value ...}}.J'ai essayé avec une fonction différente et ça marche toujours bien, mais comment exactement l'ellipse ... produire une séquence agrégée ici? – Nick

+0

ne remplit-il pas le tableau en cours d'exécution? – Nick

+2

@Nick - Les ellipses développent le composant précédent avec 'Is'; dans notre cas 'fib :: value'. Donc 'vals {{fib :: value ...}}' devient 'vals {{<0, T> :: valeur, fib <1, T> :: value, fib <2, T> :: value, fib <3, T> :: value, fib <4, T> :: value , [...] fib <18, T> :: value, fib <19, T> :: value}} ' – max66

1

Les modèles sont évalués au moment de la compilation, il n'y a donc pas de solution avec les modèles qui fonctionnent à l'exécution.

Vous pouvez créer une fonction constexpr, que peut être évaluée au moment de la compilation, en fonction de la valeur transmise. Évidemment, une valeur d'exécution peut ne pas être calculée au moment de la compilation, car elle n'est pas connue au moment de la compilation.