为什么我不能使用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' 

我正在阅读Ron Penton的“游戏程序员的数据结构” ,作者通过一个float ,但是当我尝试它时,似乎没有编译。

目前的C ++标准不允许使用float (即实数)或string文字作为模板非types参数 。 你当然可以使用floatchar *types作为普通的参数。

也许作者正在使用不符合当前标准的编译器?

简单的答案

该标准不允许将浮点作为非types模板参数 ,这可以在C ++ 11标准的以下部分中了解;

14.3.2 / 1模板非types参数[temp.arg.nontype]

非types的非模板模板参数的模板参数应该是以下之一:

  • 对于整型或枚举types的非types模板参数,模板参数types的转换后的常量expression式(5.19)

  • 非types模板参数的名称; 要么

  • 一个常量expression式(5.19),指定具有静态存储持续时间和外部或内部链接的对象的地址,或者具有外部或内部链接的函数,包括函数模板和函数模板id,但不包括非静态类成员,括号)作为&id-expression,除了如果名称引用函数或数组,则可以省略&,如果相应的模板参数是引用,则省略& 要么

  • 一个常量expression式,其值为空指针值(4.10); 要么

  • 一个常量expression式,其值为空成员指针值(4.11); 要么

  • 一个指向成员的指针,如5.3.1所述。


但..但是..为什么!?

这可能是由于浮点计算不能以精确的方式来表示。 如果被允许,这样做可能会导致错误/怪异的行为;

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

我们打算两次调用相同的函数,但这可能不是这样,因为两个计算的浮点表示不能保证完全相同


我如何将浮点值表示为模板参数?

使用C++11你可以编写一些相当先进的常量expression式constexpr )来计算浮点值编译时间的分子/分母,然后将这两个参数作为单独的整数parameter passing。

请记住定义某种阈值,以便浮点值彼此接近,得到相同的分子/分母 ,否则就没有意义,因为它会产生前面提到的相同结果,作为不允许将浮点值视为非types的原因模板参数

只是为了说明这是一个限制的原因之一(至less在当前的标准中)。

当匹配模板特化时,编译器会匹配模板参数,包括非types参数。

就其性质而言,浮点值不是精确的,它们的实现不是由C ++标准指定的。 因此,很难确定何时两个浮点非types参数真正匹配:

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

这些expression式不一定产生相同的“位模式”,因此不可能保证他们使用相同的专业化 – 没有特殊的措辞来覆盖这一点。

的确,你不能使用float文字作为模板参数。 参见14.1节 (“非types模板参数应具有下列标准之一(可选cv-qualified)…”)

您可以使用对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; 

如果您确定每个types的固定默认值,您可以创build一个types来将其定义为一个常量,并根据需要进行专门化。

 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可以是一个模板variables,它在语法上有点干净。

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

将参数作为constexprs包装在自己的类中。 实际上,这与使用一组浮动参数化类的特性相似。

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

然后创build一个将类types作为参数的模板

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

这样编译器就可以保证只有一个代码实例被创build,每个模板实例化都使用相同的参数包。 这可以解决所有问题,并且可以在模板类中使用花车和双打作为constexpr。

你总是可以伪造它

 #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。

例如,假定2位精度(除以100),可以如下创build具有1.75的增长因子的数组。

 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,那么你总是可以把它包装在一些macros中。

 #define FloatToIntPrecision(f,p) (f*(10^p)) template <typename _Kind_, int _Factor_=FloatToIntPrecision(1.75,2)> // ...