Perché non posso usare il valore float come parametro del template?

Quando provo a usare float come parametro template, il compilatore grida per questo codice, mentre int funziona bene.

È perché non posso usare float come parametro template?

 #include using namespace std; template  class GenericClass { private: T value; public: GenericClass() { value = defaultValue; } T returnVal() { return value; } }; int main() { GenericClass  gcInteger; GenericClass  gcFlaot; cout << "\n sum of integer is "<<gcInteger.returnVal(); cout << "\n sum of float is "<<gcFlaot.returnVal(); return 0; } 

Errore:

 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' 

Sto leggendo “Strutture dati per programmatori di giochi” di Ron Penton, l’autore passa un float , ma quando lo provo non sembra compilare.

L’attuale standard C ++ non consente valori letterali float (cioè numeri reali) o stringhe di caratteri da utilizzare come parametri non di tipo template . Ovviamente puoi usare i tipi float e char * come argomenti normali.

Forse l’autore sta usando un compilatore che non segue lo standard attuale?

LA SEMPLICE RISPOSTA

Lo standard non consente i punti mobili come argomenti modello non di tipo , che possono essere letti nella sezione seguente dello standard C ++ 11;

14.3.2 / 1 Argomenti non di tipo template [temp.arg.nontype]

Un argomento modello per un parametro di modello non di tipo, non modello deve essere uno di:

  • per un parametro template non di tipo di tipo integrale o di enumerazione, un’espressione costante convertita (5.19) del tipo del parametro template;

  • il nome di un parametro template non di tipo; o

  • un’espressione costante (5.19) che designa l’indirizzo di un object con durata dell’archiviazione statica e collegamento interno o esterno o una funzione con collegamento esterno o interno, inclusi modelli di funzione e id modello di funzione ma esclusi i membri di class non statici, espressi (ignorando parentesi) come & id-expression, eccetto che il & può essere omesso se il nome si riferisce a una funzione o array e deve essere omesso se il parametro template corrispondente è un riferimento; o

  • un’espressione costante che valuta un valore di puntatore nullo (4.10); o

  • un’espressione costante che valuta un valore del puntatore membro nullo (4.11); o

  • un puntatore al membro express come descritto in 5.3.1.


Ma .. ma .. PERCHÉ !?

Probabilmente è dovuto al fatto che i calcoli in virgola mobile non possono essere rappresentati in modo esatto. Se fosse permesso, potrebbe / comporterebbe un comportamento erroneo / strano quando si fa qualcosa come questo;

 func<1/3.f> (); func<2/6.f> (); 

Volevamo chiamare la stessa funzione due volte, ma questo potrebbe non essere il caso poiché la rappresentazione in virgola mobile dei due calcoli non è garantita per essere esattamente la stessa cosa.


Come dovrei rappresentare i valori in virgola mobile come argomenti del template?

Con C++11 è ansible scrivere alcune espressioni costanti piuttosto avanzate ( constexpr ) che calcolerebbero il numeratore / denominatore di un tempo di compilazione a valore flottante e quindi passare questi due come argomenti interi separati.

Ricorda di definire una sorta di soglia in modo che i valori in virgola mobile vicini tra loro restituiscano lo stesso numeratore / denominatore , altrimenti è un po ‘inutile poiché restituirà lo stesso risultato precedentemente menzionato come motivo per non consentire valori in virgola mobile come non-tipo argomenti del modello .

Giusto per fornire uno dei motivi per cui questa è una limitazione (nello standard attuale almeno).

Quando si abbinano le specializzazioni dei modelli, il compilatore corrisponde agli argomenti del modello, inclusi gli argomenti non di tipo.

Per la loro stessa natura, i valori in virgola mobile non sono esatti e la loro implementazione non è specificata dallo standard C ++. Di conseguenza, è difficile decidere quando due argomenti non di tipo floating point corrispondono realmente:

 template  void foo () ; void bar () { foo< (1.0/3.0) > (); foo< (7.0/21.0) > (); } 

Queste espressioni non producono necessariamente lo stesso “schema di bit” e quindi non sarebbe ansible garantire che abbiano usato la stessa specializzazione – senza parole speciali per coprire questo.

In effetti, non puoi usare valori letterali float come parametri del modello. Vedere la sezione 14.1 (“Un parametro modello non di tipo deve avere uno dei seguenti tipi (facoltativamente qualificati per CV) …”) dello standard.

È ansible utilizzare un riferimento al float come parametro del modello:

 template  class GenericClass . . float const c_four_point_six = 4.6; // at global scope . . GenericClass < float, c_four_point_six> gcFlaot; 

Avvolgere i parametri nella propria class come constexprs. In effetti questo è simile a un tratto in quanto parametrizza la class con un set di float.

 class MyParameters{ public: static constexpr float Kd =1.0f; static constexpr float Ki =1.0f; static constexpr float Kp =1.0f; }; 

e quindi creare un modello prendendo il tipo di class come parametro

  template  class PidController { // define short hand constants for the PID tuning parameters static constexpr NUM Kp = TUNING_PARAMS::Kp; static constexpr NUM Ki = TUNING_PARAMS::Ki; static constexpr NUM Kd = TUNING_PARAMS::Kd; .... code to actually do something ... }; 

e poi usalo così …

 int main (){ PidController controller; ... ... } 

Ciò consente al compilatore di garantire che venga creata solo una singola istanza del codice per ogni istanza di modello con lo stesso pacchetto di parametri. Questo risolve tutti i problemi e puoi usare float e raddoppiare come constexpr all’interno della class basata su modelli.

Se si ha un predefinito fisso per tipo, è ansible creare un tipo per definirlo come costante e specializzarlo secondo necessità.

 template  struct MyTypeDefault { static const T value; }; template  const T MyTypeDefault::value = T(); template <> struct MyTypeDefault { static const double value; }; const double MyTypeDefault::value = 1.0; template  class MyType { public: MyType() { value = MyTypeDefault::value; } private: T value; }; 

Se hai C ++ 11 puoi usare constexpr quando definisci il valore predefinito. Con C ++ 14, MyTypeDefault può essere una variabile di modello che è sintatticamente un po ‘più pulita.

 //C++14 template  constexpr T MyTypeDefault = T(); template <> constexpr double MyTypeDefault = 1.0; template  class MyType { private: T value = MyTypeDefault; }; 

Puoi sempre fingere …

 #include  template  struct Float { static constexpr float value() { return (float)NUM / (float)DEN; } static constexpr float VALUE = value(); }; template  struct LinearFunc { static float func(float x) { return GRAD::VALUE*x + CONST::VALUE; } }; int main() { // Y = 0.333 x + 0.2 // x=2, y=0.866 std::cout < < " func(2) = " << LinearFunc, Float<1,5> > ::func(2) < < std::endl; } 

Rif: http://code-slim-jim.blogspot.jp/2013/06/c11-no-floats-in-templates-wtf.html

Se non hai bisogno che il doppio sia una costante in fase di compilazione, puoi passarlo come puntatore:

 #include  extern const double kMyDouble = 0.1;; template  void writeDouble() { std::cout < < *MyDouble << std::endl; } int main() { writeDouble<&kMyDouble>(); return 0; } 

Se si desidera rappresentare solo una precisione fissa, è ansible utilizzare una tecnica come questa per convertire un parametro float in un int.

Ad esempio un array con un fattore di crescita di 1,75 può essere creato come segue assumendo 2 cifre di precisione (dividere per 100).

 template  class Array { public: static const float Factor; _Kind_ * Data; int Size; // ... void Resize() { _Kind_ * data = new _Kind_[(Size*Factor)+1]; // ... } } template const float Array<_kind_ ,_Factor_>::Factor = _Factor_/100; 

Se non ti piace la rappresentazione di 1,75 come 175 nell’elenco degli argomenti del modello, potresti sempre racchiuderlo in alcune macro.

 #define FloatToIntPrecision(f,p) (f*(10^p)) template  // ...