Wenn ich versuche, float
als Vorlagenparameter zu verwenden, schreit der Compiler nach diesem Code, während er int
einwandfrei funktioniert.
Liegt es daran, dass ich nicht float
als Vorlagenparameter verwenden kann?
#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'
Ich lese "Data Structures for Game Programmers" von Ron Penton, der Autor gibt ein float
, aber wenn ich es versuche, scheint es nicht zu kompilieren.
c++
templates
generics
floating-point
Yokks
quelle
quelle
float
einen Nicht-Typ-Vorlagenparameter ? In welchem Kapitel ist das?Antworten:
Der aktuelle C ++ - Standard erlaubt nicht, dass
float
Literale (dh reelle Zahlen) oder Zeichenkettenliterale als nicht typisierte Vorlagenparameter verwendet werden . Sie können die Typenfloat
und natürlichchar *
als normale Argumente verwenden.Vielleicht verwendet der Autor einen Compiler, der nicht dem aktuellen Standard entspricht?
quelle
template<char ...cs>
, kann das Zeichenfolgenliteral zur Kompilierungszeit in ein solches Paket konvertiert werden. Hier ist eine Demo zu ideone . (Demo ist C ++ 14, aber es ist einfach, es zurück auf C ++ 11 zu portieren -std::integer_sequence
ist die einzige Schwierigkeit)char &*
als Vorlagenparameter verwenden können, wenn Sie das Literal an einer anderen Stelle definieren. Funktioniert ziemlich gut als Workaround.DIE EINFACHE ANTWORT
Der Standard erlaubt keine Gleitkommazahlen als nicht typisierte Vorlagenargumente , über die im folgenden Abschnitt des C ++ 11-Standards gelesen werden kann.
Aber .. aber .. WARUM!?
Dies liegt wahrscheinlich daran, dass Gleitkommaberechnungen nicht exakt dargestellt werden können. Wenn es erlaubt wäre, könnte / würde es zu fehlerhaftem / seltsamem Verhalten führen, wenn man so etwas tut;
Wir wollten dieselbe Funktion zweimal aufrufen, dies ist jedoch möglicherweise nicht der Fall, da die Gleitkommadarstellung der beiden Berechnungen nicht garantiert exakt gleich ist.
Wie würde ich Gleitkommawerte als Vorlagenargumente darstellen?
Mit
C++11
könnten Sie einige ziemlich fortgeschrittene Konstantenausdrücke ( constexpr ) schreiben , die den Zähler / Nenner einer Kompilierungszeit mit schwebendem Wert berechnen und diese beiden dann als separate ganzzahlige Argumente übergeben.Denken Sie daran, eine Art Schwellenwert zu definieren, damit Gleitkommawerte nahe beieinander denselben Zähler / Nenner ergeben . Andernfalls ist dies irgendwie sinnlos, da dann dasselbe Ergebnis erzielt wird, das zuvor als Grund genannt wurde, Gleitkommawerte nicht als Nicht-Typ zuzulassen Vorlagenargumente .
quelle
<ratio>
in §20.10 als "Rationale Arithmetik zur Kompilierungszeit" beschrieben. Was genau zu Ihrem Beispiel passt.<ratio>
? In Zähler / Nenner umzuwandeln ?12345 * 12345
ist? (Es tut erlaubtint
sogar Template - Parameter , obwohl es nicht die Breite eines signed int angeben ist oder ob dieser Ausdruck ist UB.)Nur um einen der Gründe anzugeben, warum dies eine Einschränkung ist (zumindest im aktuellen Standard).
Beim Abgleichen von Vorlagenspezialisierungen stimmt der Compiler mit den Vorlagenargumenten überein, einschließlich nicht typisierter Argumente.
Gleitkommawerte sind naturgemäß nicht genau und ihre Implementierung ist nicht im C ++ - Standard festgelegt. Infolgedessen ist es schwierig zu entscheiden, wann zwei Gleitkomma-Argumente ohne Typ wirklich übereinstimmen:
Diese Ausdrücke erzeugen nicht notwendigerweise das gleiche "Bitmuster", so dass nicht garantiert werden kann, dass sie dieselbe Spezialisierung verwendet haben - ohne spezielle Formulierung, um dies abzudecken.
quelle
==
Operator verbieten :-) Wir akzeptieren diese Ungenauigkeit bereits zur Laufzeit, warum nicht auch zur Kompilierungszeit?In der Tat können Sie keine Float-Literale als Vorlagenparameter verwenden. Siehe Abschnitt 14.1 ("Ein Vorlagenparameter ohne Typ muss einen der folgenden (optional lebenslaufqualifizierten) Typen haben ...") der Norm.
Sie können einen Verweis auf den Float als Vorlagenparameter verwenden:
quelle
Wickeln Sie die Parameter in ihre eigene Klasse als constexprs ein. Tatsächlich ähnelt dies einem Merkmal, da es die Klasse mit einer Reihe von Floats parametrisiert.
Erstellen Sie anschließend eine Vorlage, die den Klassentyp als Parameter verwendet
und dann benutze es so ...
Auf diese Weise kann der Compiler sicherstellen, dass für jede Vorlageninstanziierung mit demselben Parameterpaket nur eine einzige Instanz des Codes erstellt wird. Damit werden alle Probleme umgangen, und Sie können Floats und Doubles als Constexpr innerhalb der Vorlagenklasse verwenden.
quelle
Wenn Sie einen festen Standardwert pro Typ festlegen möchten, können Sie einen Typ erstellen, um ihn als Konstante zu definieren und nach Bedarf zu spezialisieren.
Wenn Sie über C ++ 11 verfügen, können Sie constexpr verwenden, um den Standardwert zu definieren. Mit C ++ 14 kann MyTypeDefault eine Vorlagenvariable sein, die syntaktisch etwas sauberer ist.
quelle
Die anderen Antworten geben gute Gründe an, warum Sie wahrscheinlich keine Gleitkomma-Vorlagenparameter möchten, aber der eigentliche Deal-Braker IMO ist, dass Gleichheit mit '==' und bitweise Gleichheit nicht dasselbe sind:
-0.0 == 0.0
, aber0.0
und-0.0
sind nicht bitweise gleichNAN != NAN
Keine der beiden Arten von Gleichheit ist ein guter Kandidat für die Typgleichheit: Natürlich macht Punkt 2 die Verwendung
==
für die Bestimmung der Typgleichheit ungültig. Man könnte stattdessen bitweise Gleichheit verwenden,x != y
impliziert dies aber nichtMyClass<x>
undMyClass<y>
es handelt sich um verschiedene Typen (um 2), was ziemlich seltsam wäre.quelle
Sie können es immer vortäuschen ...
Ref: http://code-slim-jim.blogspot.jp/2013/06/c11-no-floats-in-templates-wtf.html
quelle
float
! = Rationale Zahl. Die beiden sind sehr unterschiedliche Ideen. Einer wird über eine Mantisse und einen Exponenten berechnet, der andere ist ein Rational - nicht jeder Wert, der durch einen Rational darstellbar ist, ist durch a darstellbarfloat
.float
ist definitiv eine rationale Zahl, aber es gibtfloat
s, die nicht als Verhältnisse von zweiint
s dargestellt werden können. Die Mantisse ist eineWenn das Double keine Konstante zur Kompilierungszeit sein muss, können Sie es als Zeiger übergeben:
quelle
Ab C ++ 20 ist dies möglich .
Dies gibt auch die Antwort auf die ursprüngliche Frage:
Weil es noch niemand im Standard implementiert hat. Es gibt keinen fundamentalen Grund.
In C ++ 20 können nicht typisierte Vorlagenparameter jetzt Floats und sogar Klassenobjekte sein.
Es gibt einige Anforderungen an Klassenobjekte (sie müssen vom Typ Literal sein ) und einige andere Anforderungen, um die pathologischen Fälle auszuschließen, z. B. den benutzerdefinierten Operator == ( Details ).
Wir können sogar verwenden
auto
Beachten Sie, dass GCC 9 (und 10) Vorlagenparameter vom Typ Nicht-Typ implementiert, jedoch noch nicht für Floats .
quelle
Wenn Sie nur eine feste Genauigkeit darstellen möchten, können Sie mit einer solchen Technik einen float-Parameter in einen int konvertieren.
Zum Beispiel könnte ein Array mit einem Wachstumsfaktor von 1,75 wie folgt erstellt werden, wobei eine Genauigkeit von 2 Stellen angenommen wird (dividiert durch 100).
Wenn Ihnen die Darstellung von 1,75 als 175 in der Liste der Vorlagenargumente nicht gefällt, können Sie sie jederzeit in ein Makro einschließen.
quelle
...::Factor = _Factor_/100.0;
sonst wird es Integer - Division sein.