为什么我不能使用浮点值作为模板参数?

时间:2021-05-23 21:03:20

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的《游戏程序员的数据结构》,作者通过了一个浮点数,但是当我尝试它的时候,它似乎并没有被编译。

9 个解决方案

#1


30  

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?

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

#2


107  

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;

    对于一个非类型模板参数的积分或枚举类型,一个转换常量表达式(5.19)的模板参数类型;

  • 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

    一个常数表达式(5.19),指定一个对象的地址与静态存储时间和外部或内部与外部或内部链接,链接或一个函数包括函数模板和模板id但不包括非静态类成员函数,表示(忽略圆括号)& id-expression除了&可以省略,如果名称是指一个函数或数组并应省略了相应的模板参数是参考;或

  • a constant expression that evaluates to a null pointer value (4.10); or

    计算为空指针值的常量表达式(4.10);或

  • a constant expression that evaluates to a null member pointer value (4.11); or

    计算为空成员指针值的常量表达式(4.11);或

  • a pointer to member expressed as described in 5.3.1.

    一个指向成员的指针,如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 exactly the 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.

请记住定义某种阈值,以便使浮点值接近于彼此,从而产生相同的分子/分母,否则它是没有意义的,因为它将产生先前提到的相同结果,作为不允许浮点值作为非类型模板参数的原因。

#3


31  

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.

这些表达式不一定产生相同的“位模式”,因此不可能保证它们使用相同的专门化——没有特别的措词来掩盖这一点。

#4


16  

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.

实际上,您不能将float文本用作模板参数。参见第14.1节(“非类型模板参数应该有以下(可选的cv限定)类型之一”)。

You can use a reference to the float as a template parameter:

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

#5


4  

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

#6


4  

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.

将参数(s)包装在它们自己的类中作为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.

这允许编译器保证为每个模板实例化使用相同的参数包只创建一个代码实例。这就解决了所有问题,您可以在模板类中使用float和double作为constexpr。

#7


1  

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

#8


1  

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

#9


0  

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).

例如,一个具有1.75的增长因子的数组可以被创建如下假设2位数的精度(除以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;

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)>
// ...

#1


30  

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?

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

#2


107  

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;

    对于一个非类型模板参数的积分或枚举类型,一个转换常量表达式(5.19)的模板参数类型;

  • 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

    一个常数表达式(5.19),指定一个对象的地址与静态存储时间和外部或内部与外部或内部链接,链接或一个函数包括函数模板和模板id但不包括非静态类成员函数,表示(忽略圆括号)& id-expression除了&可以省略,如果名称是指一个函数或数组并应省略了相应的模板参数是参考;或

  • a constant expression that evaluates to a null pointer value (4.10); or

    计算为空指针值的常量表达式(4.10);或

  • a constant expression that evaluates to a null member pointer value (4.11); or

    计算为空成员指针值的常量表达式(4.11);或

  • a pointer to member expressed as described in 5.3.1.

    一个指向成员的指针,如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 exactly the 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.

请记住定义某种阈值,以便使浮点值接近于彼此,从而产生相同的分子/分母,否则它是没有意义的,因为它将产生先前提到的相同结果,作为不允许浮点值作为非类型模板参数的原因。

#3


31  

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.

这些表达式不一定产生相同的“位模式”,因此不可能保证它们使用相同的专门化——没有特别的措词来掩盖这一点。

#4


16  

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.

实际上,您不能将float文本用作模板参数。参见第14.1节(“非类型模板参数应该有以下(可选的cv限定)类型之一”)。

You can use a reference to the float as a template parameter:

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

#5


4  

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

#6


4  

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.

将参数(s)包装在它们自己的类中作为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.

这允许编译器保证为每个模板实例化使用相同的参数包只创建一个代码实例。这就解决了所有问题,您可以在模板类中使用float和double作为constexpr。

#7


1  

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

#8


1  

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

#9


0  

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).

例如,一个具有1.75的增长因子的数组可以被创建如下假设2位数的精度(除以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;

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)>
// ...