1

我有一个模板类在 Visual Studio 6 中运行良好,但在更现代的版本中失败了。

template<double B, double C>
class MyClass
{
    double k(double x) const
    {
        x = fabs(x);
        if (x < 1.0)
            return ((2.0 - 1.5*B - C) * x*x*x) + ((-3.0 + 2.0*B + C) * x*x) + (1.0 - 0.33333333*B);
        if (x < 2.0)
            return ((-0.16666667*B - C) * x*x*x) + ((B + 5.0*C) * x*x) + ((-2.0*B - 8.0*C) * x) + (1.3333333*B + 4.0*C);
        return 0.0;
    }
};

error C2993: 'double' : illegal type for non-type template parameter 'B'

我明白了,不允许使用double常量作为模板参数的标准,VC++ 终于符合了。但我应该改用什么?该表达式被重复评估并且是一个真正的瓶颈,我希望在编译时而不是运行时计算常量。

4

2 回答 2

1

问题在于将模板参数与程序范围的实例化集匹配取决于精确相等,这基于机器对浮点值的特定表示可能因平台而异。(并且由于编译器不需要提供目标机器的编译时仿真,因此编译时和运行时可能会有所不同。)

解决方案是用一些其他类型的明确表示来替换double,例如比率、超过固定数量(例如一百万)的分数,或者您自己的带有整数尾数和指数的浮点样式。

在 C++11 中,您可以应用constexpr函数评估,在这种情况下会消除模板:

struct MyClass {
    constexpr MyClass( double b, double c )
        : b15( b * 1.5 ), b20( b * 2.0 ), b03( b * 0.33333333 ),
        /* etc */

    double b15, b20, b03, /* etc */

    double k( double x ) const {
        x = fabs(x);
        if (x < 1.0)
            return ((2.0 - b15 - C) * x*x*x) + ((-3.0 + b20 + C) * x*x) + (1.0 - b03);
        /* etc */
    }
};

这会预先计算常量子表达式,并有效地将它们作为单个公共函数的运行时参数。但是参数/成员对象访问可能会减慢它的速度。

如果该函数始终可以内联,则该替代方案可能会以更少的努力产生与原始模板相似的结果。

于 2013-11-14T06:00:07.627 回答
0

如果您使用 constexpr 函数,它应该以必须使用 ?: 运算符的最低成本完成您想做的所有事情,因为您只能有一个 return 语句:

constexpr double k(double B, double C, double x) const
{
    x = fabs(x);
    return 
          (x < 1.0) ? 
               ((2.0 - 1.5*B - C) * x*x*x) + ((-3.0 + 2.0*B + C) * x*x) + (1.0 - 0.33333333*B)
        : (x < 2.0) ?
           ((-0.16666667*B - C) * x*x*x) + ((B + 5.0*C) * x*x) + ((-2.0*B - 8.0*C) * x)
           + 1.3333333*B + 4.0*C)
        : 0.0;
}

但这要求 x 是文字(在编译时已知)

不幸的是,它还要求您不要按照

http://msdn.microsoft.com/en-us/library/vstudio/hh567368.aspx

如果 x 是变量(在运行时更改)或者您使用 VS:

只需删除constexpr上面的关键字。大多数优化很可能只是因为 B 和 C 在编译时已知这一事实而发生 - 检查 asm 以进行验证。(升级不仅增加了严格性,还可能改进了优化)

于 2013-11-14T07:33:43.593 回答