使用float作为非类型的模板特化

时间:2010-09-27 03:13:56

标签: c++ templates non-type

  

C++ Templates的第4.3节   州'无法使用   浮点文字(简单   常量浮点表达式)   因为模板参数具有历史性   原因'。

类似地,

  

$ 14.1 / 7州 - “非类型   模板参数不得   宣称有浮点,   class或void类型。 [例如:

template<double d> class X; // error
template<double* pd> class Y; // OK
template<double& rd> class Z; // OK"
  1. 上述引文中正在讨论的历史原因是什么?

  2. 看看为什么Y和Z有效而不是X,是否有关于使用浮点类型的非类型模板参数对指针/引用做任何事情的整个挑战?

  3. 为什么模板非类型参数不能是类类型?

5 个答案:

答案 0 :(得分:10)

由于可能的舍入错误,可能很难选择正确的模板即时关系。

请考虑以下事项:

template<float n> 
void f(n) {...}  //Version 1

template<0.3333> 
void f() { ...} // Version 2:Specialization for 0.3333 

f(1/3); - &gt;将调用哪个版本?

请考虑以下代码:

template <float f> class foo { ... }; 
foo<1E6 + 1E-6> my_foo; 

“编译器应该生成什么?编​​译器必须知道目标的详细信息 浮点架构能够运行实例化模板。 如果编译器在目标上运行,这很容易 架构,它可以只做计算,找出答案,但是 如果你是交叉编译,编译器必须能够合成 每个预期目标体系结构的浮点行为。和 值得庆幸的是,标准委员会认为这是不合理的。 “

无耻地here.

复制
  

为什么模板非类型参数不能是类类型

根据我的理解,非类型参数不能是类类型,因为可能有多个类的实现。例如

template <typename T>   
class demo{...};

template <>    
class demo<int>{...};


template <typename T, demo d> //which demo?? demo<T> or demo<int>
class Example{...};

本地类不能用作模板参数,因为they don't have external linkage

答案 1 :(得分:5)

浮点数没有通用表示(有些值甚至不能在没有精度损失的情况下表示,因为它基于近似)因此可能因平台而异,导致相同的C ++代码在不同平台上生成不同的模板。

可以说C ++支持交叉编译而不需要编译器完全模拟目标机器的浮点运算。允许float或double作为模板参数会使此无效。

template<float F> 
float squared_float() 
{ 
return F * F;
}

例如,squared_float&lt; 1.0&gt;可能与squared_float&lt; 1.00000001&gt;的功能相同在某些实现中,而在其他实现中,它们将是两个不同的功能。


reference to a float意味着编译器可以优化它,因为它知道它的值并且它永远不会改变。

对于pointer,它只是另一种与体系结构相关的(32位/ 64位)数据类型,与float无关。

答案 2 :(得分:1)

浮点值的确切编码更容易受到各个CPU的怪癖影响。例如,在评估所谓的常量表达式时,CPU应该使用更高精度的80位CPU寄存器,并且最后只能回到64位吗?如果一个编译器说“是”而另一个编译器没有 - 模板将获得两个不同的实例。但是,其他一些编译器可能只有64位寄存器,也许不同的CPU可能会因epsilon值而异。某些编译器选择评估表达式的顺序,或者是否使用浮点仿真库等编译库可能会导致此类错误匹配。此外,浮点数有一些奇怪的边缘情况:正0和负0等,必须定义行为。

这些问题可能会在不同机器(具有不同的CPU,编译器版本和标志等)上编译对象的环境中发生,但需要可靠地链接。企业通常这样做,二进制分布式库也面临这样的问题。 C ++编译器通常尝试采用一些尽可能在版本和环境中保持一致的应用程序二进制接口(ABI),但这些目前还不能标准化浮点参数的计算方式,并且如果没有例如它们,它们是如何实现的。期望所有编译器使用相同的软件浮点仿真来获取值。这需要协调工作,现有的仿真解决方案可能会产生许可问题。

有趣的是,Walter Bright(数字火星)认为这些都是垃圾并允许D中的浮点常数...猜测他已经获得了对C ++社区有用的后果的真实世界经验,但是我没有最近听说过。

答案 3 :(得分:1)

这个问题的解决方案是使用有理数。发送两个整数非类型参数,然后在构造函数中初始化float,如下所示

template<int dNum =1, int dDen = 3>
class myclass {
  double d;
  myclass: d(dNum/dDen) {}
};
瞧,通过float

答案 4 :(得分:0)

此问题的一种可能解决方案是使用具有常量值(浮点数)的类型,然后将该类型用作模板参数。例如,如果你想要一个整数多项式,比如说,并希望在某个编译时浮点值评估它:

template <int a, int b, int c>
class Polynomial {
public:
    template <typename t>
    static constexpr float eval() {
        return a*t::value*t::value + b*t::value + c;
    }
};

class THREE_POINT_FIVE {
public:
    static constexpr float value = 3.5f;
};

int main() {
    constexpr float y = Polynomial<2, 0, 1>::typename eval<THREE_POINT_FIVE>();
    std::cout << y << std::endl;
}

也可以使用允许创建浮点类的辅助类,例如%:

template <unsigned int p>
class PERCENT {
public:
    static constexpr float value = p * 0.01f;
};

...
constexpr float y2 = Polynomial<2, 0, 1>::typename eval<PERCENT<43>>
...

我想这与使用前面提到的std::ratio类似。