假设我有一个简单的函数可以执行以下操作:
template<typename T>
T get_half(T a){
return 0.5*a;
}
通常会在T为double或float的情况下评估此函数。 该标准规定0.5将是double(浮点数为0.5f)。 如何编写上面的代码,以便0.5将始终为T类型,以便在评估产品或返回时没有强制转换? 我想要的是0.5在编译时是T类型的常量。这个问题的关键是我想在运行时避免转换。
例如,如果我写:
template<typename T>
T get_half(T a){
return T(0.5)*a;
}
我能完全确定在编译时评估T(0.5)吗? 如果没有,那么实现这一目标的正确方法是什么?如果需要,我可以使用c ++ 11。
提前谢谢。
在c ++ 11中,我有一个numeric_traits类,如下所示(在头文件中)
template<typename Scalar>
struct numeric_traits{
static constexpr Scalar one_half = 0.5;
//Many other useful constants ....
};
所以在我的代码中我会用它作为:
template<typename T>
T get_half(T a){
return numeric_traits<T>::one_half*a;
}
这就是我想要的,即0.5在编译时以我需要的精度解析,并且在运行时不会发生强制转换。但缺点是:
再次感谢您。
答案 0 :(得分:2)
没有任何方法可以强制常量永远不会在运行时计算,因为有些机器根本没有一条指令可以加载一个类型的所有可能值。例如,机器可能只有一个16位加载常量指令,其中0x12345678
需要在运行时计算为0x1234 << 16 | 0x5678
。或者,可以从内存中加载这样的常量,但这可能比计算它的成本更高。
您需要相信您的编译器。在可行的系统上,任何具有任何优化量的编译器都将翻译T(0.5)
,其转换方式与0.5f
相同,假设T
为float
。并且0.5f
将以最合理的方式为您的平台计算。这可能涉及将其作为常量加载,或者可能涉及计算它。或者谁知道,如果结果相同,您的编译器可能会将T(0.5)*a
更改为a/2
。
在您的问题中,您举一个添加numeric_traits
助手类的示例。这个,IMO,有点矫枉过正。在constexpr
产生影响的极不可能的情况下,你可以写
template <typename T>
T get_half(T a) {
constexpr T half = 0.5;
return half * a;
}
然而,在我看来,这仍然弊大于利:您的get_half
现在不再可以用于非文字类型。 需要类型以支持常量表达式中double
的转换。假设您有一个任意精度的rational
类型,在没有constexpr
的情况下编写。现在,您的get_half
无法使用,因为初始化constexpr T half = 0.5;
无效,即使0.5 * a
可能已编译。
即使你的numeric_traits
助手类也是如此;它只是因为我把它移到了函数体中而没有效。