Vedo che le altre risposte trattano bene gli approcci alternativi, ma nessuno ha spiegato perché è richiesto lo enum
(o static const int
).
In primo luogo, si consideri il seguente modello di non-equivalenti:
#include <iostream>
int Factorial(int n)
{
if (n == 0)
return 1;
else
return n * Factorial(n-1);
}
int main()
{
std::cout << Factorial(5) << std::endl;
std::cout << Factorial(10) << std::endl;
}
Si dovrebbe essere in grado di capire facilmente. Tuttavia, lo svantaggio è che il valore del fattoriale verrà calcolato in fase di esecuzione, vale a dire dopo aver eseguito il programma, il compilatore eseguirà le chiamate e i calcoli della funzione ricorsiva.
L'idea dell'approccio modello è eseguire gli stessi calcoli in fase di compilazione e posizionare il risultato nell'eseguibile risultante. In altre parole, l'esempio che ha presentato risolve a qualcosa di simile:
int main()
{
std::cout << 120 << std::endl;
std::cout << 3628800 << std::endl;
}
Ma al fine di ottenere che, bisogna 'trucco' il compilatore a compiere i calcoli. E per farlo, devi lasciare che memorizzi il risultato da qualche parte.
Il enum
è esattamente lì per farlo. Cercherò di spiegarlo sottolineando che non funzionerebbe lì.
Se si è tentato di utilizzare un normale int
, non funzionerebbe perché un membro non statico come int
è significativo solo in un oggetto istanziato. E non puoi assegnare un valore come questo ma invece farlo in un costruttore. Un semplice int
non funzionerà.
Hai bisogno di qualcosa che sarebbe accessibile su una classe non documentata. Potresti provare static int
ma ancora non funziona.clang
darebbe una descrizione abbastanza semplice del problema:
c.cxx:6:14: error: non-const static data member must be initialized out of line
static int value=n*Factorial<n-1>::value ;
^ ~~~~~~~~~~~~~~~~~~~~~~~
Se effettivamente messo queste definizioni out-of-line, il codice verrà compilato ma si tradurrà in due 0
s. Questo perché questo modulo ritarda il calcolo dei valori all'inizializzazione del programma e non garantisce l'ordine corretto. È probabile che sia stato ottenuto un Factorial<n-1>::value
s prima di essere calcolato, e quindi è stato restituito 0
. Inoltre, non è ancora ciò che realmente vogliamo.
Infine, se si inserisce static const int
lì, funzionerà come previsto. Questo perché static const
deve essere calcolato al momento della compilazione, ed è esattamente ciò che vogliamo. Proviamo quindi a digitare di nuovo il codice:
#include <iostream>
template <unsigned n>
struct Factorial
{
static const int value=n*Factorial<n-1>::value ;
};
template <>
struct Factorial<0>
{
static const int value=1;
};
int main()
{
std::cout << Factorial<5>::value << std::endl;
std::cout << Factorial<10>::value << std::endl;
}
Prima si crea un'istanza Factorial<5>
; static const int
obbliga il compilatore a calcolare il suo valore al momento del compilatore. In effetti, crea un'istanza del tipo Factorial<4>
quando deve calcolare un altro valore. E questo va uno fino a quando non colpisce Factorial<0>
dove il valore può essere calcolato senza ulteriori istanziazioni.
Quindi, quello era il modo alternativo e la spiegazione. Spero che sia stato almeno un po 'utile per capire il codice.
Si può pensare a quel tipo di modelli come una sostituzione della funzione ricorsiva che ho postato all'inizio. Basta sostituire:
return x;
con static const int value = ...
,
f(x-1)
con t<x-1>::value
,
- e
if (n == 0)
con la specializzazione struct Factorial<0>
.
E per il enum
stessa, come è stato già sottolineato, è stato utilizzato nell'esempio di applicare lo stesso comportamento static const int
. È così perché tutti i valori enum
devono essere conosciuti in fase di compilazione, così efficacemente ogni valore richiesto deve essere calcolato in fase di compilazione.
Questo in realtà non risponde alla domanda, motivo per cui ha usato "enum". – Puppy
@Nawaz a quanto pare conosce la risposta, ma non lo dice chiaramente. In alcuni compilatori, 'static const int' è ** non garantito ** una costante in fase di compilazione, perché lo standard pre-C++ 11 non richiede al compilatore di fare un tentativo esaustivo per risolverlo. Quindi, provare a ** usare ** un tale valore come argomento template, come in 'Factorial', fallirà perché il compilatore potrebbe aver deciso di non rendere questa istanza di 'value' a constexpr. –
rwong