C++ 为什么我不能使用浮点值作为模板参数?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2183087/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why can't I use float value as a template parameter?
提问by yokks
When I try to use float
as a template parameter, the compiler cries for this code, while int
works fine.
当我尝试float
用作模板参数时,编译器会为这段代码哭泣,但int
工作正常。
Is it because I cannot use float
as a template parameter?
是因为我不能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;
}
Error:
错误:
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'
I am reading "Data Structures for Game Programmers"by Ron Penton, the author passes a float
, but when I try it it doesn't seem to compile.
我正在阅读Ron Penton 的“游戏程序员的数据结构”,作者通过了一个float
,但是当我尝试它时,它似乎无法编译。
采纳答案by yokks
The current C++ standard does not allow float
(i.e. real number) or character string literals to be used as template non-type parameters. You can of course use the float
and char *
types as normal arguments.
当前的 C++ 标准不允许float
(即实数)或字符串文字用作模板非类型参数。您当然可以使用float
和char *
类型作为普通参数。
Perhaps the author is using a compiler that doesn't follow the current standard?
也许作者使用的编译器不符合当前标准?
回答by Filip Roséen - refp
THE SIMPLE ANSWER
简单的答案
The standard doesn't allow floating points as non-type template-arguments, which can be read about in the following section of the C++11 standard;
该标准不允许浮点作为非类型模板参数,可以在 C++11 标准的以下部分阅读;
14.3.2/1 Template non-type arguments [temp.arg.nontype]
A template-argument for a non-type, non-template template-parameter shall be one of:
for a non-type template-parameter of integral or enumeration type, a converted constant expression (5.19) of the type of the template-parameter;
the name of a non-type template-parameter; or
a constant expression (5.19) that designates the address of an object with static storage duration and external or internal linkage or a function with external or internal linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as & id-expression, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference; or
a constant expression that evaluates to a null pointer value (4.10); or
a constant expression that evaluates to a null member pointer value (4.11); or
a pointer to member expressed as described in 5.3.1.
14.3.2/1 模板非类型参数 [temp.arg.nontype]
非类型、非模板模板参数的模板参数应为以下之一:
对于整型或枚举类型的非类型模板参数,模板参数类型的转换常量表达式(5.19);
非类型模板参数的名称;或者
一个常量表达式 (5.19),它指定具有静态存储持续时间和外部或内部链接的对象或具有外部或内部链接的函数的地址,包括函数模板和函数模板 ID,但不包括非静态类成员,表示(忽略括号)作为 & id 表达式,但如果名称指的是函数或数组,则 & 可以省略,如果相应的模板参数是引用,则应省略;或者
计算结果为空指针值的常量表达式 (4.10);或者
计算结果为空成员指针值的常量表达式 (4.11);或者
5.3.1 中描述的成员指针。
But.. but.. WHY!?
但是……但是……为什么!?
It is probably due to the fact that floating point calculations cannot be represented in an exact manner. If it was allowed it could/would result in erroneous/weird behavior when doing something as this;
这可能是由于浮点计算无法以精确方式表示。如果允许,在做这样的事情时可能/会导致错误/奇怪的行为;
func<1/3.f> ();
func<2/6.f> ();
We meant to call the same function twice but this might not be the case since the floating point representation of the two calculations isn't guaranteed to be exactlythe same.
我们打算调用同一个函数两次,但情况可能并非如此,因为不能保证两次计算的浮点表示完全相同。
How would I represent floating point values as template arguments?
我将如何将浮点值表示为模板参数?
With C++11
you could write some pretty advanced constant-expressions(constexpr) that would calculate the numerator/denominator of a floating value compile time and then pass these two as separate integer arguments.
随着C++11
你可以写一些非常先进的常量表达式(constexpr),将计算出的浮动值,编译时的分子/分母,然后通过这两个作为单独的整数参数。
Remember to define some sort of threshold so that floating point values close to each other yields the same numerator/denominator, otherwise it's kinda pointless since it will then yield the same result previously mentioned as a reason not to allow floating point values as non-type template arguments.
请记住定义某种阈值,以便彼此接近的浮点值产生相同的分子/分母,否则它有点毫无意义,因为它将产生与前面提到的相同的结果作为不允许浮点值作为 非类型的原因模板参数。
回答by Richard Corden
Just to provide one of the reasons why this is a limitation (in the current standard at least).
只是为了提供这是一个限制的原因之一(至少在当前标准中)。
When matching template specializations, the compiler matches the template arguments, including non-type arguments.
当匹配模板特化时,编译器匹配模板参数,包括非类型参数。
By their very nature, floating point values are not exact and their implementation is not specified by the C++ standard. As a result, it is difficult to decide when two floating point non type arguments really match:
就其本质而言,浮点值并不精确,并且 C++ 标准未指定它们的实现。因此,很难确定两个浮点非类型参数何时真正匹配:
template <float f> void foo () ;
void bar () {
foo< (1.0/3.0) > ();
foo< (7.0/21.0) > ();
}
These expressions do not necessarily produce the same "bit pattern" and so it would not be possible to guarantee that they used the same specialization - without special wording to cover this.
这些表达式不一定会产生相同的“位模式”,因此无法保证它们使用相同的专业化 - 没有特殊措辞来涵盖这一点。
回答by moonshadow
Indeed, you can't use float literals as template parameters. See section 14.1("A non-type template-parameter shall have one of the following (optionally cv-qualified) types...")of the standard.
实际上,您不能使用浮点文字作为模板参数。请参阅标准的第 14.1 节(“非类型模板参数应具有以下(可选的 cv 限定)类型之一......”)。
You can use a reference to the float as a template parameter:
您可以使用对浮点数的引用作为模板参数:
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;
回答by Andrew Goedhart
Wrap the parameter(s) in their own class as constexprs. Effectively this is similar to a trait as it parameterizes the class with a set of floats.
将参数作为 constexprs 包装在它们自己的类中。实际上,这类似于特征,因为它使用一组浮点数对类进行参数化。
class MyParameters{
public:
static constexpr float Kd =1.0f;
static constexpr float Ki =1.0f;
static constexpr float Kp =1.0f;
};
and then create a template taking the class type as a parameter
然后创建一个以类类型为参数的模板
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 ...
};
and then use it like so...
然后像这样使用它......
int main (){
PidController<float, MyParameters> controller;
...
...
}
This allows the compiler to guarantee that only a single instance of the code is created for each template instantiation with the same parameter pack. That gets around all the issues and you are able to use floats and doubles as constexpr inside the templated class.
这允许编译器保证对于每个具有相同参数包的模板实例,只创建代码的单个实例。这解决了所有问题,您可以在模板化类中使用浮点数和双精度数作为 constexpr。
回答by Matthew Fioravante
If you are ok to have a fixed default per type you can create a type to define it as a constant and specialize it as needed.
如果您可以为每种类型设置一个固定的默认值,您可以创建一个类型来将其定义为常量并根据需要对其进行专门化。
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;
};
If you have C++11 you can use constexpr when defining the default value. With C++14, MyTypeDefault can be a template variable which is a bit cleaner syntactically.
如果你有 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>;
};
回答by user3233025
If you don't need the double to be a compile-time constant, you can pass it in as a pointer:
如果不需要 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;
}
回答by Ashley Smart
You can always fake it...
你总是可以伪造它...
#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
参考:http: //code-slim-jim.blogspot.jp/2013/06/c11-no-floats-in-templates-wtf.html
回答by jurujen
If you only want to represent a fixed precision, then you can use a technique like this to convert a float parameter into an int.
如果您只想表示固定精度,那么您可以使用这样的技术将浮点参数转换为 int。
For example an array with a growth factor of 1.75 could be created as follows assuming 2 digits of precision (divide by 100).
例如,假设 2 位精度(除以 100),可以按如下方式创建增长因子为 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;
If you dont like the representation of 1.75 as 175 in the template argument list then you could always wrap it in some macro.
如果您不喜欢在模板参数列表中将 1.75 表示为 175,那么您始终可以将其包装在某个宏中。
#define FloatToIntPrecision(f,p) (f*(10^p))
template <typename _Kind_, int _Factor_=FloatToIntPrecision(1.75,2)>
// ...
回答by Matthieu
The other answers give good reasons why you probably do not want floating point template parameters, but the real deal braker IMO is that equality using '==' and bitwise equality are not the same:
其他答案给出了您可能不想要浮点模板参数的充分理由,但真正的交易制动器 IMO 是使用 '==' 的相等性和按位相等性是不一样的:
-0.0 == 0.0
, but0.0
and-0.0
are not bitwise equalNAN != NAN
-0.0 == 0.0
, 但0.0
和-0.0
不按位相等NAN != NAN
Neither kind of equality is a good cancidate for type equality:
Of course, point 2. makes using ==
invalid for determining type equality.
One could use bitwise equality instead, but then x != y
does not imply that MyClass<x>
and MyClass<y>
are different types (by 2.), which would be rather strange.
两种相等都不是类型相等的好候选:当然,第 2 点使 using==
用于确定类型相等无效。人们可以使用按位平等代替,但x != y
并不意味着MyClass<x>
与MyClass<y>
不同类型(由2),这将是很奇怪的。