widzę, że pozostałe odpowiedzi pokrycie alternatywnych podejść dobrze, ale nikt nie wyjaśnił, dlaczego konieczne jest enum
(lub static const int
).
Po pierwsze, rozważmy następujący zakaz szablonu równoważne:
#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;
}
powinieneś być w stanie go zrozumieć łatwo. Jednak wadą jest to, że wartość silni zostanie obliczona w czasie wykonywania, tj. Po uruchomieniu programu kompilator wykona rekursywne wywołania funkcji i obliczenia.
Ideą podejścia szablonowego jest wykonanie tych samych obliczeń w czasie kompilacji i umieszczenie wyniku w wynikowym pliku wynikowym. Innymi słowy, jesteś przykładem prezentowane postanawia coś zarówno:
int main()
{
std::cout << 120 << std::endl;
std::cout << 3628800 << std::endl;
}
Jednak aby to osiągnąć, trzeba „trik” kompilator do wykonywania obliczeń. Aby to zrobić, musisz pozwolić mu na przechowywanie gdzieś wyniku.
Urządzenie enum
jest dokładnie w tym celu. Spróbuję to wytłumaczyć, wskazując, że nie będzie tam działać .
Jeśli próbowałeś użyć zwykłego int
, to nie działałoby, ponieważ niestatyczny element, taki jak int
, ma znaczenie tylko w instancji obiektu. I nie można przypisać takiej wartości do tego, ale zamiast tego zrobić to w konstruktorze. Zwykły int
nie będzie działać.
Potrzebujesz czegoś, co będzie dostępne w nieumiejętnej klasie. Możesz spróbować static int
, ale nadal nie działa.clang
nie daje dość prosty opis problemu:
c.cxx:6:14: error: non-const static data member must be initialized out of line
static int value=n*Factorial<n-1>::value ;
^ ~~~~~~~~~~~~~~~~~~~~~~~
Jeśli faktycznie umieścić te definicje out-of-line, kod zostanie skompilowany, ale spowoduje to dwie 0
s. Dzieje się tak dlatego, że ta forma opóźnia obliczenie wartości do momentu inicjalizacji programu i nie gwarantuje poprawnej kolejności. Prawdopodobnie przed obliczeniem otrzymano Factorial<n-1>::value
s, a więc zwrócono 0
. Co więcej, nadal nie jest to, czego naprawdę chcemy.
Wreszcie, jeśli umieścisz tam static const int
, będzie działać zgodnie z oczekiwaniami. To dlatego, że static const
musi zostać obliczone w czasie kompilacji i właśnie tego chcemy. Załóżmy wpisać kod jeszcze:
#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;
}
najpierw utworzyć instancję Factorial<5>
; static const int
zmusza kompilator do obliczenia jego wartości w czasie kompilacji. W efekcie tworzy instancję typu Factorial<4>
, gdy ma obliczyć inną wartość. I to idzie jeden, aż trafi Factorial<0>
, gdzie wartość można obliczyć bez dalszych wystąpień.
To była alternatywna droga i wyjaśnienie. Mam nadzieję, że było to co najmniej pomocne w zrozumieniu kodu.
Możesz myśleć o tego rodzaju szablony jako zamiennik funkcji rekurencyjnej, którą zamieściłem na początku. Wystarczy wymienić:
return x;
z static const int value = ...
,
f(x-1)
z t<x-1>::value
,
- i
if (n == 0)
ze specjalizacją struct Factorial<0>
.
A dla samego enum
, jak to już zauważył, że został użyty w przykładzie egzekwować takie samo zachowanie jak static const int
. Jest tak, ponieważ wszystkie wartości enum
muszą być znane podczas kompilacji, więc efektywnie każda wymagana wartość musi zostać obliczona podczas kompilacji.
To właściwie nie odpowiada na pytanie, dlatego użył słowa "enum". – Puppy
@Nawaz najwyraźniej zna odpowiedź, ale nie określił jej wyraźnie. W niektórych kompilatorach 'statyczna const int' nie jest ** gwarantowana ** stała czasu kompilacji, ponieważ standard pre-C++ 11 nie wymaga od kompilatora do podjęcia wyczerpującej próby jego rozwiązania. Zatem, próba ** użycia ** takiej "wartości" jako argumentu szablonu, jak w 'Factorial', nie powiedzie się, ponieważ kompilator mógł zdecydować, że nie uczyni tego wystąpienia wartości "constexpr". –
rwong