Dlaczego nie mogę użyć wartości float jako parametru szablonu?

Kiedy próbuję użyć float jako parametru szablonu, kompilator płacze za ten kod, podczas gdy int działa dobrze.

Czy dlatego, że nie mogę użyć float jako parametru szablonu?

#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;       
}

Błąd:

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'

Czytam "struktury danych dla programistów gier" autorstwa Rona Pentona, autor przekazuje float, ale kiedy próbuję, nie wydaje się, aby kompilować.

Author: Filip Roséen - refp, 2010-02-02

11 answers

Obecny standard C++ nie pozwala float (tj. liczba rzeczywista) lub literały ciągu znaków być używane jakoszablon Nie-parametry typu . Możesz oczywiście używać typów float i char * jako normalnych argumentów.

Być może autor używa kompilatora, który nie jest zgodny z obecnym standardem?

 40
Author: Filip Roséen - refp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-13 05:30:54

PROSTA ODPOWIEDŹ

Standard nie dopuszcza punktów zmiennoprzecinkowych jako nie - typowych argumentów szablonu , o czym można przeczytać w poniższej sekcji standardu C++11;

14.3.2 / 1 Szablon argumenty Nie typu [temp.arg.nontype]

A template-argument for a non-type, non-template template-parameter jest jednym z:

  • Dla nie-typowego szablonu-parametru typu całkowego lub wyliczeniowego, przeliczone wyrażenie stałe (5.19) z rodzaju template-parameter;

  • Nazwa parametru szablonu innego niż typ; lub

  • Wyrażenie stałe (5.19), które wyznacza adres obiektu ze statycznym czasem przechowywania i zewnętrznym lub wewnętrznym połączeniem lub funkcji z połączeniem zewnętrznym lub wewnętrznym, w tym funkcji templates and function template-ids but except non-static class członkowie, wyrażeni (ignorując nawiasy) jako & ID-wyrażenie, z wyjątkiem że & może być pominięte, jeśli nazwa odnosi się do funkcji lub tablicy i pomija się, jeśli odpowiedni parametr szablonu jest odniesienie; lub

  • Wyrażenie stałe, które oblicza wartość wskaźnika null (4.10); lub

  • Wyrażenie stałe, które oblicza wartość wskaźnika elementu null (4.11); lub

  • Wskaźnik do członu wyrażony zgodnie z opisem w pkt 5.3.1.


Ale.. ale.. Dlaczego!?

Jest to prawdopodobnie spowodowane fakt, że obliczenia zmiennoprzecinkowe nie mogą być przedstawione w sposób dokładny. Gdyby to było dozwolone, mogłoby / spowodowałoby błędne / dziwne zachowanie podczas robienia czegoś takiego; {]}

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

Zamierzaliśmy wywołać tę samą funkcję dwa razy, ale może tak nie być, ponieważ reprezentacja zmiennoprzecinkowa obu obliczeń nie jest gwarantowana, że będzie dokładnie taka sama.


Jak reprezentować wartości zmiennoprzecinkowe jako argumenty szablonu?

Z C++11 możesz napisz kilka dość zaawansowanych stałych wyrażeń (constexpr), który obliczy licznik / mianownik zmiennej wartości, a następnie przekaże te dwa jako oddzielne argumenty całkowite.

Pamiętaj, aby zdefiniować jakiś próg tak, aby wartości zmiennoprzecinkowe blisko siebie dawały ten sam Licznik / mianownik, w przeciwnym razie jest to trochę bezcelowe, ponieważ daje to ten sam wynik, o którym wcześniej wspomniano jako powód, aby nie zezwalać na zmiennoprzecinkowe wartości jako niepodpisane argumenty szablonu .

 142
Author: Filip Roséen - refp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55

Wystarczy podać jeden z powodów, dla których jest to ograniczenie (przynajmniej w obecnym standardzie).

Podczas dopasowywania specjalizacji szablonu kompilator dopasowuje argumenty szablonu, włączając w to argumenty nie-typowe.

Ze względu na swoją naturę, wartości zmiennoprzecinkowe nie są dokładne, a ich implementacja nie jest określona przez standard C++. W rezultacie trudno jest zdecydować, czy dwa argumenty zmiennoprzecinkowe nie są zgodne:

template <float f> void foo () ;

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

Wyrażenia te do niekoniecznie wytwarzają ten sam "wzór bitowy", więc nie można zagwarantować, że używają tej samej specjalizacji - bez specjalnego sformułowania, które to obejmuje.

 34
Author: Richard Corden,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-02 16:46:37

Rzeczywiście, nie można używać liter float jako parametrów szablonu. Patrz sekcja 14.1 (" parametr szablonu innego niż typ ma jeden z następujących typów (opcjonalnie kwalifikowanych przez cv)...") standardu.

Możesz użyć odniesienia do float jako parametru szablonu:

template <class T, T const &defaultValue>
class GenericClass

.
.

float const c_four_point_six = 4.6; // at global scope

.
.

GenericClass < float, c_four_point_six> gcFlaot;
 20
Author: moonshadow,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-02 10:03:00

Zawiń parametr(y) w ich własnej klasie jako constexprs. W rzeczywistości jest to podobne do cechy, ponieważ parametryzuje klasę za pomocą zestawu pływaków.

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

A następnie Utwórz szablon przyjmujący typ klasy jako parametr

  template <typename NUM, typename TUNING_PARAMS >
  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 ...
};

A następnie użyj go w ten sposób...

int main (){
    PidController<float, MyParameters> controller;
    ...
    ...
}

Pozwala to kompilatorowi zagwarantować, że tylko jedna instancja kodu jest tworzona dla każdej instancji szablonu z tym samym pakietem parametrów. To się wokół wszystkich problemów i jesteś w stanie aby używać float i doubles jako constexpr wewnątrz klasy template.

 13
Author: Andrew Goedhart,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-16 11:03:32

Jeśli nie masz nic przeciwko temu, aby mieć stałą domyślną dla każdego typu, możesz utworzyć taki typ, aby zdefiniować go jako stałą i specjalizować w razie potrzeby.

template <typename T> struct MyTypeDefault { static const T value; };
template <typename T> const T MyTypeDefault<T>::value = T();
template <> struct MyTypeDefault<double> { static const double value; };
const double MyTypeDefault<double>::value = 1.0;

template <typename T>
class MyType {
  public:
    MyType() { value = MyTypeDefault<T>::value; }
  private:
    T value;
 };

Jeśli masz C++11, możesz użyć constexpr podczas definiowania wartości domyślnej. W C++14, MyTypeDefault może być zmienną szablonową, która jest nieco czystsza składniowo.

//C++14
template <typename T> constexpr T MyTypeDefault = T();
template <> constexpr double MyTypeDefault<double> = 1.0;

template <typename T>
class MyType {
  private:
    T value = MyTypeDefault<T>;
 };
 6
Author: Matthew Fioravante,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-30 16:00:43

Począwszy od C++20 jest to Możliwe .

To również daje odpowiedź na pierwotne pytanie:

Why can't I use float value as a template parameter?

Bo nikt jeszcze nie zaimplementował tego w standardzie. Nie ma żadnego fundamentalnego powodu.

W C++20 Nie-typowych parametrów szablonu mogą być teraz floatami, a nawet obiektami klasy.

Istnieją pewne wymagania dotyczące obiektów klasy (muszą one być typem literalnym ) i spełniać inne wymagania, aby wykluczyć przypadki patologiczne, takie jak user zdefiniowany operator = = (Details ).

Możemy nawet użyć auto

template <auto Val>
struct Test {
};

struct A {};
static A aval;
Test<aval>  ta;
Test<A{}>  ta2;
Test<1.234>  tf;
Test<1U>  ti;

Zauważ, że GCC 9 (i 10) implementuje nie-typowe parametry szablonu klasy, , ale nie dla floatów jeszcze .

 4
Author: Andreas H.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-07-08 11:45:21

Inne odpowiedzi dają dobre powody, dla których prawdopodobnie nie chcesz parametrów szablonu zmiennoprzecinkowego, ale tak naprawdę braker IMO jest taki, że równość za pomocą "= = " i bitowa równość nie są takie same:

  1. -0.0 == 0.0, ale 0.0 i -0.0 nie są równe bitowo

  2. NAN != NAN

Żaden rodzaj równości nie jest dobry dla równości typu: Oczywiście, punkt 2. sprawia, że użycie == jest niepoprawne do określania równości typów. Można użyć bitowe równość zamiast, ale wtedy x != y nie oznacza, że MyClass<x> i MyClass<y> są różnymi typami (przez 2.), co byłoby raczej dziwne.

 2
Author: Matthieu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-05-10 19:07:55

Zawsze możesz udawać...

#include <iostream>

template <int NUM, int DEN>
struct Float
{
    static constexpr float value() { return (float)NUM / (float)DEN; }
    static constexpr float VALUE = value();
};

template <class GRAD, class CONST>
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,3>, Float<1,5> > ::func(2) << std::endl;
}

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

 1
Author: Ashley Smart,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-08 17:43:52

Jeśli nie potrzebujesz podwójnego, aby była stałą w czasie kompilacji, możesz przekazać go jako wskaźnik:

#include <iostream>

extern const double kMyDouble = 0.1;;

template <const double* MyDouble>
void writeDouble() {
   std::cout << *MyDouble << std::endl; 
}

int main()
{
    writeDouble<&kMyDouble>();
   return 0;
}
 1
Author: user3233025,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-07 18:53:20

Jeśli chcesz reprezentować tylko stałą precyzję, możesz użyć takiej techniki, aby przekonwertować parametr float na int.

Na przykład tablica o współczynniku wzrostu 1,75 może być utworzona w następujący sposób przy założeniu 2 cyfr dokładności (podziel przez 100).

template <typename _Kind_, int _Factor_=175>
class Array
{
public:
    static const float Factor;
    _Kind_ * Data;
    int Size;

    // ...

    void Resize()
    {
         _Kind_ * data = new _Kind_[(Size*Factor)+1];

         // ...
    }
}

template<typename _Kind_, int _Factor_>
const float Array<_kind_,_Factor_>::Factor = _Factor_/100;

Jeśli nie podoba Ci się reprezentacja 1.75 jako 175 na liście argumentów szablonu wtedy zawsze można owinąć go w jakieś makro.

#define FloatToIntPrecision(f,p) (f*(10^p))

template <typename _Kind_, int _Factor_=FloatToIntPrecision(1.75,2)>
// ...
 0
Author: jurujen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-02 16:43:04