Почему я не могу использовать значение типа float в качестве параметра шаблона?



когда я пытаюсь использовать float в качестве параметра шаблона компилятор запрашивает этот код, в то время как int работает нормально.



это потому, что я не могу использовать float как параметр шаблона?



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


ошибка:



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'


Я читаю "структуры данных для игровых программистов" Рон Пентон, автор передает a float, но когда я пытаюсь это сделать, он, кажется, не компилируется.

771   9  

9 ответов:

текущий стандарт C++ не допускает float (т. е. действительное число) или символьные строковые литералы, которые будут использоваться как параметры шаблона не-типа. Вы можете, конечно, использовать float и char * типы как нормальные аргументы.

возможно, автор использует компилятор, который не соответствует текущему стандарту?

ПРОСТОЙ ОТВЕТ

стандарт не допускает плавающие точки как шаблон без типа-аргументы, который можно прочитать в следующем разделе стандарта C++11;

14.3.2 / 1 шаблон не тип аргументов [temp.аргумент.нетиповые]

шаблон-аргумент для шаблона без типа, без шаблона-параметр должен быть один из:

  • для шаблона без типа-параметр интеграла или тип перечисления, преобразованное константное выражение (5.19) типа шаблон-параметр;

  • имя шаблона-параметра без типа; или

  • постоянное выражение (5.19), которое обозначает адрес объекта со статической длительностью хранения и внешней или внутренней связью или функция с внешней или внутренней связью, включая функцию шаблоны и идентификаторы шаблонов функций, но исключая нестатический класс члены, выраженные (игнорируя круглые скобки) как & id-выражение, за исключением что & может быть опущен, если имя ссылается на функцию или массив и должен быть опущен, если соответствующий шаблон-параметр a ссылка; или

  • постоянное выражение, которое вычисляет нулевое значение указателя (4.10); или

  • постоянное выражение, которое вычисляет значение указателя нулевого члена (4.11); или

  • указатель на члены выразили, как описано в 5.3.1.


но.. но.. Зачем??

вероятно, это связано с тем, что вычисления с плавающей запятой не могут быть представлены точно. Если бы это было разрешено, это могло бы / привело бы к ошибочному / странному поведению при выполнении чего-то подобного;

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

мы хотели вызвать одну и ту же функцию дважды, но это может быть не так, так как представление с плавающей запятой двух расчеты не гарантируется быть ровно то же самое.


Как бы я представлял значения с плавающей запятой в качестве аргументов шаблона?

С C++11 вы могли бы написать некоторые довольно продвинутые константы-выражения (constexpr), что бы вычислить числитель/знаменатель плавающего значения времени компиляции, а затем передать эти два в качестве отдельных целочисленных аргументов.

Не забудьте определить какой-то порог так что значения с плавающей запятой близко друг к другу дает то же самое числитель/знаменатель, в противном случае это своего рода бессмысленно, так как это приведет к тому же результату, который ранее упоминался в качестве причины не разрешать значения с плавающей запятой как аргументы шаблона без типа.

просто чтобы указать одну из причин, почему это ограничение (по крайней мере, в текущем стандарте).

при сопоставлении специализаций шаблона компилятор сопоставляет аргументы шаблона, включая аргументы без типов.

по самой своей природе значения с плавающей запятой не являются точными, и их реализация не задается стандартом C++. В результате трудно решить, когда два аргумента с плавающей запятой без типа действительно матч:

template <float f> void foo () ;

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

эти выражения не обязательно производят один и тот же" битовый шаблон", и поэтому было бы невозможно гарантировать, что они использовали одну и ту же специализацию - без специальной формулировки, чтобы покрыть это.

действительно, Вы не можете использовать литералы с плавающей запятой в качестве параметров шаблона. Смотрите 14.1("шаблон-параметр, не относящийся к типу, должен иметь один из следующих типов (необязательно CV-квалифицированный)...") стандартные.

вы можете использовать ссылку на float в качестве параметра шаблона:

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;

оберните параметр (ы) в свой собственный класс как constexprs. Фактически это похоже на признак, поскольку он параметризует класс с набором поплавков.

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

а затем создать шаблон, принимая тип класса в качестве параметра

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

а затем использовать его так...

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

это позволяет компилятору гарантировать, что для каждого экземпляра шаблона создается только один экземпляр кода с одним и тем же пакетом параметров. Это становится вокруг все проблемы, и вы можете использовать поплавки и удваивается как constexpr внутри шаблонного класса.

Если вы в порядке, чтобы иметь фиксированное значение по умолчанию для каждого типа, вы можете создать тип, чтобы определить его как константу и специализировать его по мере необходимости.

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

Если у вас есть C++11, вы можете использовать constexpr при определении значения по умолчанию. С C++14 MyTypeDefault может быть переменной шаблона, которая немного чище синтаксически.

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

вы всегда можете поддельные его...

#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

Если вам не нужно, чтобы double был константой времени компиляции, вы можете передать его в качестве указателя:

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

Если вы хотите только представить фиксированную точность, то вы можете использовать такой метод, чтобы преобразовать параметр float в int.

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;

Если вам не нравится представление 1.75 как 175 в списке аргументов шаблона тогда вы всегда можете обернуть его в какой-то макрос.

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

template <typename _Kind_, int _Factor_=FloatToIntPrecision(1.75,2)>
// ...

Comments

    Ничего не найдено.