Cuando intento usarlo float
como parámetro de plantilla, el compilador pide este código, mientras que int
funciona bien.
¿Es porque no puedo usarlo float
como parámetro de plantilla?
#include<iostream>
using namespace std;
template <class T, T defaultValue>
class GenericClass
{
private:
T value;
public:
GenericClass()
{
value = defaultValue;
}
T returnVal()
{
return value;
}
};
int main()
{
GenericClass <int, 10> gcInteger;
GenericClass < float, 4.6f> gcFlaot;
cout << "\n sum of integer is "<<gcInteger.returnVal();
cout << "\n sum of float is "<<gcFlaot.returnVal();
return 0;
}
Error:
main.cpp: In function `int main()':
main.cpp:25: error: `float' is not a valid type for a template constant parameter
main.cpp:25: error: invalid type in declaration before ';' token
main.cpp:28: error: request for member `returnVal' in `gcFlaot',
which is of non-class type `int'
Estoy leyendo "Estructuras de datos para programadores de juegos" de Ron Penton, el autor pasa un float
, pero cuando lo intento no parece compilarse.
c++
templates
generics
floating-point
yokks
fuente
fuente
float
como un parámetro de plantilla que no es de tipo ? ¿En qué capítulo es ese?Respuestas:
El estándar actual de C ++ no permite
float
(es decir, números reales) o literales de cadenas de caracteres que se utilicen como parámetros de plantilla que no sean de tipo . Por supuesto, puede utilizar los tiposfloat
ychar *
como argumentos normales.¿Quizás el autor está usando un compilador que no sigue el estándar actual?
fuente
template<char ...cs>
, entonces el literal de cadena se puede convertir en dicho paquete en el momento de la compilación. Aquí hay una demostración de ideone . (La demostración es C ++ 14, pero es fácil trasladarla a C ++ 11,std::integer_sequence
es la única dificultad)char &*
como parámetro de plantilla si define el literal en otro lugar. Funciona bastante bien como solución.LA RESPUESTA SIMPLE
El estándar no permite puntos flotantes como argumentos de plantilla que no sean de tipo , sobre los cuales se puede leer en la siguiente sección del estándar C ++ 11;
Pero ... pero ... ¿¡POR QUÉ !?
Probablemente se deba al hecho de que los cálculos de coma flotante no se pueden representar de manera exacta. Si estuviera permitido, podría / resultaría en un comportamiento erróneo / extraño al hacer algo como esto;
Queríamos llamar a la misma función dos veces, pero este podría no ser el caso, ya que no se garantiza que la representación de punto flotante de los dos cálculos sea exactamente la misma.
¿Cómo representaría valores de punto flotante como argumentos de plantilla?
Con
C++11
podría escribir algunas expresiones constantes bastante avanzadas ( constexpr ) que calcularían el numerador / denominador de un tiempo de compilación de valor flotante y luego pasarían estos dos como argumentos enteros separados.Recuerde definir algún tipo de umbral para que los valores de punto flotante cercanos entre sí produzcan el mismo numerador / denominador ; de lo contrario, no tiene sentido, ya que producirá el mismo resultado mencionado anteriormente como una razón para no permitir valores de punto flotante como no tipo argumentos de plantilla .
fuente
<ratio>
describe en §20.10 como "Aritmética racional en tiempo de compilación". Lo que va directo a tu ejemplo.<ratio>
?12345 * 12345
es? (Se hace permitir queint
los parámetros de plantilla a pesar de que no especifica el ancho de una int firmado o si dicha expresión es UB.)Solo para proporcionar una de las razones por las que esto es una limitación (al menos en la norma actual).
Al hacer coincidir especializaciones de plantilla, el compilador coincide con los argumentos de la plantilla, incluidos los argumentos que no son de tipo.
Por su propia naturaleza, los valores de coma flotante no son exactos y su implementación no está especificada por el estándar C ++. Como resultado, es difícil decidir cuándo coinciden realmente dos argumentos que no son de tipo de punto flotante:
Estas expresiones no producen necesariamente el mismo "patrón de bits", por lo que no sería posible garantizar que utilizaran la misma especialización, sin una redacción especial para cubrir esto.
fuente
==
operador :-) Ya aceptamos esta inexactitud en tiempo de ejecución, ¿por qué no también en tiempo de compilación?De hecho, no puede usar literales flotantes como parámetros de plantilla. Consulte la sección 14.1 ("Un parámetro de plantilla que no sea de tipo deberá tener uno de los siguientes tipos (opcionalmente calificado para cv) ...") del estándar.
Puede utilizar una referencia al flotante como parámetro de plantilla:
fuente
Envuelva el (los) parámetro (s) en su propia clase como constexprs. Efectivamente, esto es similar a un rasgo, ya que parametriza la clase con un conjunto de flotantes.
y luego crea una plantilla tomando el tipo de clase como parámetro
y luego úsalo así ...
Esto permite al compilador garantizar que solo se cree una única instancia del código para cada instanciación de plantilla con el mismo paquete de parámetros. Eso soluciona todos los problemas y puede usar flotadores y dobles como constexpr dentro de la clase con plantilla.
fuente
Si está de acuerdo con tener un valor predeterminado fijo por tipo, puede crear un tipo para definirlo como una constante y especializarlo según sea necesario.
Si tiene C ++ 11, puede usar constexpr al definir el valor predeterminado. Con C ++ 14, MyTypeDefault puede ser una variable de plantilla que es un poco más limpia sintácticamente.
fuente
Las otras respuestas dan buenas razones por las que probablemente no desee parámetros de plantilla de punto flotante, pero lo más importante en mi opinión es que la igualdad usando '==' y la igualdad bit a bit no son lo mismo:
-0.0 == 0.0
, pero0.0
y-0.0
no son iguales a nivel de bitsNAN != NAN
Ninguno de los dos tipos de igualdad es una buena alternativa para la igualdad de tipos: por supuesto, el punto 2.
==
invalida el uso para determinar la igualdad de tipos. En su lugar, se podría usar la igualdad bit a bit, pero esox != y
no implica esoMyClass<x>
yMyClass<y>
son tipos diferentes (por 2.), lo que sería bastante extraño.fuente
Siempre puedes fingirlo ...
Ref: http://code-slim-jim.blogspot.jp/2013/06/c11-no-floats-in-templates-wtf.html
fuente
float
! = Número racional. Las dos son ideas muy separadas. Uno se calcula mediante una mantisa y un exponente, el otro es, bueno, un racional; no todos los valores representables por un racional son representables por afloat
.float
es definitivamente un número racional, pero hayfloat
s que no se pueden representar como razones de dosint
s. La mantisa es un número entero y el exponente 2 ^ es un número enteroSi no necesita que el doble sea una constante en tiempo de compilación, puede pasarlo como un puntero:
fuente
Comenzando con C ++ 20 esto es posible .
Esto también da la respuesta a la pregunta original:
Porque nadie lo implementó en el estándar todavía. No hay ninguna razón fundamental.
En C ++ 20, los parámetros de plantilla que no son de tipo ahora pueden ser flotantes e incluso objetos de clase.
Hay algunos requisitos sobre los objetos de clase (deben ser un tipo literal ) y cumplen algunos otros requisitos para excluir los casos patológicos, como el operador definido por el usuario == ( Detalles ).
Incluso podemos usar
auto
Tenga en cuenta que GCC 9 (y 10) implementa parámetros de plantilla de clase que no son de tipo, pero todavía no para flotantes .
fuente
Si solo desea representar una precisión fija, puede usar una técnica como esta para convertir un parámetro flotante en un int.
Por ejemplo, se podría crear una matriz con un factor de crecimiento de 1,75 de la siguiente manera, asumiendo 2 dígitos de precisión (dividir por 100).
Si no le gusta la representación de 1,75 como 175 en la lista de argumentos de la plantilla, siempre puede envolverlo en alguna macro.
fuente
...::Factor = _Factor_/100.0;
contrario será una división entera.