Lorsque j'essaie de l'utiliser float
comme paramètre de modèle, le compilateur pleure ce code, alors qu'il int
fonctionne correctement.
Est-ce parce que je ne peux pas utiliser float
comme paramètre de modèle?
#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;
}
Erreur:
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'
Je lis "Structures de données pour les programmeurs de jeux" de Ron Penton, l'auteur passe un float
, mais quand je l'essaye, il ne semble pas se compiler.
float
comme paramètre de modèle non type ? Dans quel chapitre est-ce?Réponses:
Le standard C ++ actuel n'autorise pas
float
(c'est-à-dire un nombre réel) ou des chaînes de caractères littérales à être utilisées comme paramètres de modèle non-type . Vous pouvez bien sûr utiliser les typesfloat
etchar *
comme arguments normaux.Peut-être que l'auteur utilise un compilateur qui ne suit pas la norme actuelle?
la source
template<char ...cs>
, alors le littéral de chaîne peut être converti en un tel pack au moment de la compilation. Voici une démo sur ideone . (La démo est C ++ 14, mais il est facile de la porter en C ++ 11 -std::integer_sequence
c'est la seule difficulté)char &*
comme paramètre de modèle si vous définissez le littéral ailleurs. Fonctionne assez bien comme solution de contournement.LA RÉPONSE SIMPLE
La norme n'autorise pas les points flottants en tant qu'arguments de modèle non de type , qui peuvent être lus dans la section suivante de la norme C ++ 11;
Mais… mais… POURQUOI !?
Cela est probablement dû au fait que les calculs en virgule flottante ne peuvent pas être représentés de manière exacte. Si cela était autorisé, cela pourrait / entraînerait un comportement erroné / étrange en faisant quelque chose comme ceci;
Nous voulions appeler la même fonction deux fois, mais ce n'est peut-être pas le cas car la représentation en virgule flottante des deux calculs n'est pas garantie d'être exactement la même.
Comment représenterais-je des valeurs à virgule flottante comme arguments de modèle?
Avec
C++11
vous pourriez écrire des expressions constantes assez avancées ( constexpr ) qui calculeraient le numérateur / dénominateur d'une valeur flottante lors de la compilation, puis passer ces deux comme arguments entiers séparés.N'oubliez pas de définir une sorte de seuil afin que les valeurs à virgule flottante proches les unes des autres donnent le même numérateur / dénominateur , sinon c'est un peu inutile car cela donnera alors le même résultat précédemment mentionné comme raison de ne pas autoriser les valeurs à virgule flottante comme non-type arguments de modèle .
la source
<ratio>
, décrite par le § 20.10 comme «Arithmétique rationnelle à la compilation». Ce qui va droit à votre exemple.<ratio>
?12345 * 12345
c'est? (Il ne permetint
même les paramètres de modèle si elle ne précise pas la largeur d'un int signé ou si cette expression est UB.)Juste pour fournir l'une des raisons pour lesquelles il s'agit d'une limitation (au moins dans la norme actuelle).
Lors de la mise en correspondance des spécialisations de modèle, le compilateur fait correspondre les arguments de modèle, y compris les arguments non de type.
De par leur nature même, les valeurs en virgule flottante ne sont pas exactes et leur implémentation n'est pas spécifiée par la norme C ++. En conséquence, il est difficile de décider quand deux arguments non type à virgule flottante correspondent vraiment:
Ces expressions ne produisent pas nécessairement le même "motif de bits" et il ne serait donc pas possible de garantir qu'elles utilisent la même spécialisation - sans une formulation spéciale pour couvrir cela.
la source
==
opérateur :-) Nous acceptons déjà cette inexactitude au moment de l'exécution, pourquoi pas aussi au moment de la compilation?En effet, vous ne pouvez pas utiliser de littéraux flottants comme paramètres de modèle. Voir la section 14.1 ("Un paramètre de modèle non-type doit avoir l'un des types suivants (éventuellement qualifiés cv) ...") de la norme.
Vous pouvez utiliser une référence au flottant comme paramètre de modèle:
la source
Enveloppez le ou les paramètres dans leur propre classe en tant que constexprs. En fait, cela est similaire à un trait car il paramètre la classe avec un ensemble de flottants.
puis créez un modèle en prenant le type de classe comme paramètre
puis utilisez-le comme ça ...
Cela permet au compilateur de garantir qu'une seule instance du code est créée pour chaque instanciation de modèle avec le même pack de paramètres. Cela permet de contourner tous les problèmes et vous pouvez utiliser des flottants et se double de constexpr dans la classe basée sur un modèle.
la source
Si vous êtes d'accord pour avoir une valeur par défaut fixe par type, vous pouvez créer un type pour le définir comme une constante et le spécialiser selon vos besoins.
Si vous avez C ++ 11, vous pouvez utiliser constexpr lors de la définition de la valeur par défaut. Avec C ++ 14, MyTypeDefault peut être une variable de modèle dont la syntaxe est un peu plus claire.
la source
Les autres réponses donnent de bonnes raisons pour lesquelles vous ne voulez probablement pas de paramètres de modèle en virgule flottante, mais le vrai frein IMO est que l'égalité en utilisant '==' et l'égalité au niveau du bit ne sont pas les mêmes:
-0.0 == 0.0
, mais0.0
et-0.0
ne sont pas égaux au niveau du bitNAN != NAN
Aucun des deux types d'égalité n'est un bon choix pour l'égalité de type: bien sûr, le point 2 rend l'utilisation
==
invalide pour déterminer l'égalité de type. On pourrait utiliser l'égalité au niveau du bit à la place, maisx != y
cela n'implique pas celaMyClass<x>
et ilMyClass<y>
existe différents types (par 2.), ce qui serait plutôt étrange.la source
Vous pouvez toujours faire semblant ...
Réf: http://code-slim-jim.blogspot.jp/2013/06/c11-no-floats-in-templates-wtf.html
la source
float
! = Nombre rationnel. Les deux sont des idées très distinctes. L'une est calculée via une mantisse et un exposant, l'autre est, eh bien, un rationnel - toutes les valeurs représentables par un rationnel ne sont pas représentables par afloat
.float
est très certainement un nombre rationnel, mais il y a desfloat
s qui ne sont pas représentables comme des rapports de deuxint
s. La mantisse est un entier, et l'exposant 2 ^ est un entierSi vous n'avez pas besoin que le double soit une constante de compilation, vous pouvez le transmettre en tant que pointeur:
la source
À partir de C ++ 20, cela est possible .
Cela donne également la réponse à la question initiale:
Parce que personne ne l'a encore implémenté dans la norme. Il n'y a pas de raison fondamentale.
En C ++ 20, les paramètres de modèle non-type peuvent désormais être des flottants et même des objets de classe.
Il existe certaines exigences sur les objets de classe (ils doivent être de type littéral ) et remplir d'autres exigences pour exclure les cas pathologiques tels que l'opérateur défini par l'utilisateur == ( Détails ).
On peut même utiliser
auto
Notez que GCC 9 (et 10) implémente des paramètres de modèle de classe non-type, mais pas encore pour les flottants .
la source
Si vous souhaitez uniquement représenter une précision fixe, vous pouvez utiliser une technique comme celle-ci pour convertir un paramètre float en un entier.
Par exemple, un tableau avec un facteur de croissance de 1,75 pourrait être créé comme suit en supposant 2 chiffres de précision (diviser par 100).
Si vous n'aimez pas la représentation de 1,75 comme 175 dans la liste d'arguments du modèle, vous pouvez toujours l'envelopper dans une macro.
la source
...::Factor = _Factor_/100.0;
autrement, ce sera une division entière.