Почему я не могу использовать значение типа 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, но когда я пытаюсь это сделать, он, кажется, не компилируется.
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