¿Alguien puede explicarme por qué la primera plantilla de metaprogramación va a un ciclo infinito, pero la segunda se ejecuta correctamente?
#include <iostream>
using namespace std;
template<int N, int M>
struct commondivs {
static const int val = (N<M) ? commondivs<N,(M-N)>::val : commondivs<(N-M),M>::val;
};
template<int N>
struct commondivs<N,N> {
static const int val = N;
};
int commondiv(int N, int M){
if(N==M){
return N;
}
return (N<M)?commondiv(N,(M-N)):commondiv((N-M),M);
}
int main() {
cout << commondivs<9,6>::val << endl;
cout << commondiv(9,6) << endl;
return 0;
}
constexpr
no es una opinión.constexpr
no es una opción. (Fue introducido en C ++ 11). Eso invalida las respuestas existentes. Exxul, aclare a qué versión de C ++ está limitado.Respuestas:
Esta línea provoca la creación de instancias de ambos
commondivs<N,(M-N)>::val
ecommondivs<(N-M),M>::val
, incluso si la condición se conoce en el momento de la compilación y nunca se tomará una de las ramas.Reemplace
? :
constd::conditional_t
, que no tiene esta limitación:fuente
El problema es que se evaluarán todos los operandos de operador condicional, por lo tanto
commondivs<N,(M-N)>
, ycommondivs<(N-M),M>
consiguen crear instancias y suval
get evaluados y luego conduce a instancias de plantilla recursiva.Puede aplicar constexpr if y ponerlo en una
constexpr
static
función miembro.EN VIVO
fuente
::val
tiene que ser generado en ambas ramas seguro, pero esto todavía es una instanciación (de una plantilla con un miembro constante estático). La evaluación en tiempo de ejecución no ocurre ... bueno, obviamente no puede, ya que nunca se compila ...El operador ternario no es como
if constexpr
: cuando un compilador lo ve, tiene que generar código para ambas ramas. En otras palabras, para crear una instancia de una plantillacommondivs<M, N>
, un compilador crea una instancia de ambas plantillascommondivs<N, M - N>
ycommondivs<N - M, M>
.En contraste con eso,
commondiv(N, M - N)
ycommondiv(N - M, M)
se traducen en dos llamadas a funciones. Cuál se toma, se decidirá cuando se llame realmente a la función.Adición.
HolyBlackCat dio una solución con
std::conditional_t
. Aquí hay otro:fuente
Obtienes una recursión infinita porque:
no es la programación metatemplate en absoluto porque
?:
, como dice @Eng, no lo esconstexpr
.Desea ver la respuesta de @ HolyBlackCat.
fuente
?:
no esconstexpr
.