Veo que las otras respuestas cubren bien los enfoques alternativos, pero nadie explica por qué es necesario enum
(o static const int
).
En primer lugar, consideremos el siguiente plantilla no equivalentes:
#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;
}
Usted debe ser capaz de entenderlo fácilmente. Sin embargo, su desventaja es que el valor del factorial se calculará en tiempo de ejecución, es decir, después de ejecutar su programa, el compilador ejecutará las llamadas y cálculos de la función recursiva.
La idea del enfoque de plantilla es realizar los mismos cálculos en tiempo de compilación y colocar el resultado en el ejecutable resultante. En otras palabras, el ejemplo que presenta el resuelve algo parecido:
int main()
{
std::cout << 120 << std::endl;
std::cout << 3628800 << std::endl;
}
Pero para lograr eso, usted tiene que 'truco' el compilador en la realización de los cálculos. Y para hacer eso, debes dejar que almacene el resultado en alguna parte.
El enum
está ahí exactamente para hacer eso. Trataré de explicarlo indicando qué no funcionaría allí.
Si ha intentado utilizar un int
normal, no funcionaría porque un miembro no estático como int
tiene sentido solo en un objeto instanciado. Y no puede asignarle un valor así, sino que hacerlo en un constructor. Un simple int
no funcionará.
Necesita algo que sea accesible en una clase desinstalada en su lugar. Puede intentar static int
pero todavía no funciona.clang
le daría una descripción bastante sencillo 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 ;
^ ~~~~~~~~~~~~~~~~~~~~~~~
Si realmente poner esas definiciones fuera de línea, el código se compilará pero dará lugar a dos 0
s. Esto se debe a que este formulario retrasa el cálculo de valores para la inicialización del programa y no garantiza el orden correcto. Es probable que se haya obtenido Factorial<n-1>::value
s antes de calcularse y, por lo tanto, se devolvió 0
. Además, todavía no es lo que realmente queremos.
Por último, si pone static const int
allí, funcionará como se esperaba. Eso es porque static const
tiene que calcularse en el tiempo de compilación, y eso es exactamente lo que queremos. Vamos escriba el código de nuevo:
#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;
}
En primer lugar se ejemplariza Factorial<5>
; static const int
obliga al compilador a calcular su valor en el momento del compilador. Efectivamente, crea el tipo Factorial<4>
cuando tiene que calcular otro valor. Y esto va uno hasta que llegue a Factorial<0>
donde el valor puede calcularse sin más instanciaciones.
Entonces, esa era la forma alternativa y la explicación. Espero que sea al menos un poco útil para entender el código.
Puede pensar en ese tipo de plantillas como un reemplazo de la función recursiva que publiqué al principio. Usted acaba de cambiar:
return x;
con static const int value = ...
,
f(x-1)
con t<x-1>::value
,
- y
if (n == 0)
con la especialización struct Factorial<0>
.
Y para el enum
en sí, como ya se señaló, fue utilizado en el ejemplo para hacer cumplir el mismo comportamiento que static const int
. Es así porque todos los valores enum
deben conocerse en tiempo de compilación, por lo que todos los valores solicitados deben calcularse efectivamente en tiempo de compilación.
Esto no responde la pregunta, por lo que usó 'enum'. – Puppy
@Nawaz aparentemente sabe la respuesta, pero no lo dijo claramente. En algunos compiladores, 'static const int' ** no está garantizado ** una constante en tiempo de compilación, porque el estándar anterior a C++ 11 no requiere que el compilador haga un intento exhaustivo de resolverlo. Por lo tanto, tratar de ** usar ** un 'valor' como un argumento de plantilla, como en' Factorial ', fallará porque el compilador puede haber decidido no hacer de esta instancia de' valor' un constexpr. –
rwong